# AdminAihub — AI設定ハブ

CMS内のAI機能で使うプロバイダ（APIキー / ベースURL / 既定モデル）を1ページで一元管理する管理プラグイン。
OpenAI互換の `chat/completions` を持つプロバイダなら何でも使える統一設計（base_url + key + model）。

管理画面: `admin/aihub/general/index`（ナビ「AI設定」）

## 対応プロバイダ（プリセット）

| ID | プロバイダ | ベースURL | 既定モデル例 |
|---|---|---|---|
| `openai` | OpenAI | `https://api.openai.com/v1` | `gpt-4o-mini` |
| `gemini` | Google Gemini | `https://generativelanguage.googleapis.com/v1beta/openai` | `gemini-2.5-flash` |
| `anthropic` | Anthropic Claude | `https://api.anthropic.com/v1` | `claude-haiku-4-5` |
| `openrouter` | OpenRouter | `https://openrouter.ai/api/v1` | `openrouter/auto` |
| `groq` | Groq | `https://api.groq.com/openai/v1` | `llama-3.3-70b-versatile` |
| `deepseek` | DeepSeek | `https://api.deepseek.com/v1` | `deepseek-chat` |
| `ollama` | Ollama（ローカルLLM・キー不要） | `http://localhost:11434/v1` | `llama3.2` |
| `custom` | カスタム（OpenAI互換） | 任意 | 任意 |

## System値（このプラグインが保存するキー）

- プロバイダごと: `AIHUB_{PROVIDER}_KEY` / `AIHUB_{PROVIDER}_BASE` / `AIHUB_{PROVIDER}_MODEL`
  （PROVIDER = `OPENAI` / `GEMINI` / `ANTHROPIC` / `OPENROUTER` / `GROQ` / `DEEPSEEK` / `OLLAMA` / `CUSTOM`）
- 機能別割当: `AIHUB_ASSIGN_CHAT` / `AIHUB_ASSIGN_WRITE` / `AIHUB_ASSIGN_TOOLS` / `AIHUB_ASSIGN_BLOCKS`（値はプロバイダID・既定 `openai`）
- 初回取り込み済みフラグ: `AIHUB_IMPORTED`

## 既存機能との互換（既存プラグインのコードは一切変更しない）

### 初回取り込み（AI設定ページを開いたとき1回だけ）

| 取り込み元（既存System値） | 取り込み先 |
|---|---|
| `AIWRITE_OPENAI_KEY` → `AICHAT_API_KEY` → `AIMG_OPENAI_KEY` → `THEMESTUDIO_OPENAI_KEY` → `AIVID_OPENAI_KEY`（最初に見つかったもの） | `AIHUB_OPENAI_KEY` |
| `AIWRITE_MODEL` → `AICHAT_MODEL` | `AIHUB_OPENAI_MODEL` |
| `AIMG_GEMINI_KEY` | `AIHUB_GEMINI_KEY` |

### 保存時の互換書き込み

既存プラグインは `api.openai.com` 直結で動作しているため、壊さないように以下のルールで書き込む。

| ハブの値 | 書き込み先（既存機能が読むSystem値） | 条件 |
|---|---|---|
| OpenAIキー | `AICHAT_API_KEY` / `AIWRITE_OPENAI_KEY` / `AIMG_OPENAI_KEY` / `AIVID_OPENAI_KEY` / `THEMESTUDIO_OPENAI_KEY` | キーが設定されているとき常に |
| Geminiキー | `AIMG_GEMINI_KEY` | キーが設定されているとき常に |
| OpenAIの既定モデル | `AICHAT_MODEL` | 「AIチャット」の割当がOpenAIのとき |
| OpenAIの既定モデル | `AIWRITE_MODEL`（AI記事作成 / BlockAiが参照） | 「AI記事作成」の割当がOpenAIのとき |
| OpenAIの既定モデル | `THEMESTUDIO_MODEL` | 「AIブロック/テーマスタジオ」の割当がOpenAIのとき |

OpenAI **以外** のプロバイダのキー・モデルは既存System値には書き込まない
（既存コードがOpenAIのURLに投げるため、他社キーを書くと既存機能が壊れる）。
OpenAI以外の割当は、下記 `AihubClient` を利用する箇所で有効になる。

## AihubClient の使い方（将来の移行用・共有Lib）

```php
App::uses('AihubClient', 'AdminAihub.Lib');

// purpose（chat / write / tools / blocks）から割当プロバイダを解決して実行
$text = AihubClient::chat('write', array(
  array('role' => 'system', 'content' => 'あなたは記事ライターです。出力は必ずJSONのみ。'),
  array('role' => 'user',   'content' => 'テーマ: 夏の営業案内'),
), array(
  'json'        => TRUE,   // response_format: json_object
  'max_tokens'  => 8000,   // 既定 4000
  'temperature' => 0.4,    // 既定 0.7
  'timeout'     => 110,    // 秒・既定 120
  // 'provider' => 'groq', // 割当を無視して明示指定も可
  // 'model'    => '...',  // 既定モデルの上書きも可
));

// プロバイダ明示 + 詳細（応答時間・実モデル名）が欲しい場合
$r = AihubClient::request('deepseek', $messages, array('max_tokens' => 100));
// => array('content' => '...', 'model' => 'deepseek-chat', 'ms' => 812)
```

- 失敗時は日本語メッセージの `Exception` を投げる（HTTPコード別のヒント付き・APIキーは含めない）。
- `response_format: json_object` は一部プロバイダ/モデルで無視される場合がある。JSON必須の用途ではプロンプト側でもJSON出力を指示すること。
- 画像生成・音声系はこのクライアントの対象外（OpenAI / Gemini の専用APIを使う既存実装のまま）。

## 既知のエラー

| エラー内容 | 原因 | 解決策 |
|---|---|---|
| 接続テストで HTTP 404 | ベースURL末尾の誤り（`/v1` 抜け等）またはモデル名間違い | プリセットボタンでベースURLを再入力し、モデル例から選ぶ |
| Ollamaに接続できない | CMSサーバーから到達できないURL（共用サーバー→手元PCのlocalhost等） | サーバー内でOllamaを稼働させるか、到達可能な公開URLを指定 |
