extension-inference について
Caffeine AI アプリに LLM チャット機能を組み込むためのスキルです。プラットフォーム提供の OpenAI 互換 Caffeine Inference を canister から呼び出し、認証情報をブラウザーに渡さず、API キー管理やモデル選択も不要にします。
主な機能
- 01OpenAI 互換のチャット補完
- 02実行時に提供される認証情報
- 03canister 経由の推論呼び出し
- 04router によるモデル選択
- 05フロントエンドでのエラー表示と再試行
活用シーン
- 01Caffeine AI アプリへのチャット追加
- 02LLM によるテキストの要約や分類
- 03ブラウザーコードへの推論認証情報の露出防止
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ caffeinelabs/ skills --skill extension-inferenceインストール後、Claude Code で/extension-inferenceを入力して読み込まれたか確認します。
詳細
Caffeine Inference
Caffeine AI 向けの LLM 拡張機能です。
オーケストレーターのルーティングに関する注意事項
「LLM/GPT/チャットボットを使う」「AI で要約する」といった要望は、プラットフォームの標準機能として扱ってください。標準の方法は Caffeine Inference です。Caffeine がアプリ向けにホストし、認証と課金を行う、OpenAI 互換のチャットエンドポイントです。カニスターは実行時にプラットフォームから認証情報を取得します。API キーを貼り付ける必要はなく、アプリが API キーを保存したり返したりすることもありません。
| ユーザーの意図 | 機能 |
|---|---|
| Caffeine アプリで LLM を使ってチャット/要約/分類する | このスキルを使った caffeineai-inference-client の ChatApi.createChatCompletion |
ユーザーが貼り付けた sk-... を使って api.openai.com を呼び出す | extension-openai のみ |
通常の Caffeine アプリで LLM を使う場合、extension-openai を読み込まないでください。ユーザーに OpenAI API キーを求めないでください。setApiKey エンドポイントやキー設定ページ、モデル選択機能を追加しないでください。
バックエンド
1. mops.toml に caffeineai-inference-client を追加する
mops add [email protected]
Mops ≥ 2.13 が必要です。最小バージョン: caffeineai-inference-client ≥ 0.2.0。
2. 設定はプラットフォームから取得する
Config.fromEnv<system>() は、プラットフォームがアプリに提供する認証情報から、エンドポイント、ベアラー、is_replicated = ?false を含む完全な Config を返します。収集するキーはなく、設定も不要です。
- リクエストごとに、
sharedメソッド内、または<system>をパラメーター化したヘルパー内でfromEnv<system>()を呼び出してください。モジュールレベルのlet config = fromEnvはコンパイルできません。また、キャッシュしたConfigは、実行中のカニスターでプラットフォームが認証情報をローテーションした際に古くなる可能性があります。 - アプリに推論用の認証情報がない場合、トラップが発生します。そのエラーは呼び出し元に伝えてください。隠すと、AI 呼び出しの失敗が機能しているように見えてしまいます。
CAFFEINE_INFERENCE_API_KEY(またはその他の環境変数)を事前に確認しないでください。また、呼び出しをスキップするチェックでfromEnvをガードしないでください。- 失敗した呼び出しを、空文字列、空のリスト、プレースホルダーテキストなどの空の成功に変換しないでください。フロントエンドがエラーを表示して再試行を提案できるよう、トラップを発生させるかエラーを返してください。
- 「AI を設定する」ための空状態や、キー入力によるフォールバックを追加しないでください。
Configをログに記録せず、そのauthをアクターの状態にコピーせず、query/shared関数から(その一部も含めて)返さないでください。
3. is_replicated = ?false は必須です
fromEnv はすでにこれを設定しています。?true や null で上書きしないでください。
- セキュリティ。 レプリケートされたアウトコールでは、すべてのレプリカからベアラーが送信されます。
- 課金。 レプリケートされたアウトコールでは、サブネットのサイズに応じて推論費用が増加します。
- 決定性。 LLM の本文はサンプリングされるため、コンセンサスが失敗します。
4. 標準的な構成
import Inference "lib/inference";
actor {
public shared func chat(prompt : Text) : async Text {
await* Inference.runChat<system>(prompt);
};
};
import { fromEnv } "mo:caffeineai-inference-client/Config";
import ChatApi "mo:caffeineai-inference-client/Apis/ChatApi";
import ChatCompletionRequest "mo:caffeineai-inference-client/Models/ChatCompletionRequest";
import ChatCompletionRequestMessageOneOf2 "mo:caffeineai-inference-client/Models/ChatCompletionRequestMessageOneOf2";
import Runtime "mo:core/Runtime";
module {
public func runChat<system>(prompt : Text) : async* Text {
let config = fromEnv<system>();
let userMessage = ChatCompletionRequestMessageOneOf2.JSON.init({
content = #string(prompt);
role = #user;
});
let req = ChatCompletionRequest.JSON.init({
messages = [#user(userMessage)];
model = "router";
});
let resp = await* ChatApi.createChatCompletion(config, req);
if (resp.choices.size() == 0) {
Runtime.trap("Inference returned no choices");
};
resp.choices[0].message.content
?? Runtime.trap("Inference returned no text content");
};
};
5. model = "router" — モデルはプラットフォームが選択します
"router" は唯一の公開モデル ID で、モデル名ではなくルーティング層です。Caffeine Inference はクエリの複雑さに合わせて各リクエストのモデルを選択します。単純なプロンプトには高速で小型のモデルを、難しい推論にはより高性能なモデルを使います。また、レスポンスの model として "router" を返すため、プロバイダー名がアプリに渡ることはありません。
- 常に
model = "router"を送信してください。 - モデルのドロップダウン、「GPT-4 を使用」トグル、バックエンドエンドポイントの
modelパラメーターを追加しないでください。ユーザーが選択するものはありません。 - モデルの選択ではなく、プロンプトと宣言されたサンプリングフィールド(
temperature、top_p、max_completion_tokens)で品質を調整してください。
6. 呼び出し形式
- 関数形式:
ChatApi.createChatCompletion(config, req) : async*—await*を使用します。 - Suite 形式:
let api = ChatApi(config); api.createChatCompletion(req) : async。
7. 利用可能な API サーフェス — チャット補完
[email protected] は
public-api-v0.1.0 から生成されています。
| モジュール | エントリーポイント | ルート |
|---|---|---|
ChatApi | createChatCompletion | POST /v1/chat/completions |
ModelsApi | listModels | GET /v1/models — カタログ専用です。モデルは常に "router" なので、アプリでこれを使う必要はありません |
import ChatApi "mo:caffeineai-inference-client/Apis/ChatApi";
import { fromEnv } "mo:caffeineai-inference-client/Config";
チャット補完が提供する機能のすべてです。このホストでは、埋め込み、画像、音声、モデレーション、ファイル、レガシー補完、Assistants、Responses、raw ic.http_request は利用できません(404 が返され、パッケージにも含まれていません)。仕様で sk-... を貼り付けて使う OpenAI 専用 API が本当に必要な場合は、extension-openai に切り替えてください。
8. サイクル
cycles は未設定(null)のままにします。各呼び出しには、そのリクエストに対して IC が課金する手数料が適用されます。アウトコールが無料のサブネットでは、手数料はゼロです。?n を指定すると、正確に n サイクルが適用されます。n が手数料を下回ると IC が呼び出しを拒否し、キャニスターが n サイクル未満しか保有していないサブネットでは呼び出しがトラップします。
ストリーミング(stream = ?true)はサポートされていません。管理キャニスターの HTTP は本文全体を返します。stream = null のままにしてください。
9. 問題になりやすい点
sharedメソッド内(または<system>ヘルパー内)でfromEnv<system>()を呼び出します。モジュールレベルのlet config = fromEnvはコンパイルできません。- 失敗を表面化させてください。認証情報の事前チェック、
fromEnvのガード、エラーの代わりに空の結果を返すことはしないでください。§2 を参照してください。 model = "router"を指定します。"gpt-4o-mini"ではありません。§5 を参照してください。- ユーザーターンは
#user(ChatCompletionRequestMessageOneOf2.JSON.init({ content = #string(prompt); role = #user }))です。 - 必須フィールドには
JSON.initを使い、レコード更新でオプションを追加します。すべてのnullを手作業で列挙しないでください。 resp.choices[0].message.contentは?Textです。先にchoices.size()を確認してください。- 1 回のチャット呼び出しは、更新呼び出し内の 1 回の HTTP アウトコールです。ミリ秒ではなく秒単位で見積もってください。
フロントエンド
アプリは初回読み込み時からチャットできる状態です。設定は不要です。
- API キー用 UI は不要です。 設定ページ、パスワード入力欄、「設定済み?」インジケーター、localStorage はありません。仕様やモックに「AI 設定」画面がある場合は削除してください。
- モデル選択機能は不要です。 §5 を参照してください。
- バックエンドのチャットエンドポイント(
chat(prompt))を呼び出し、返されたテキストを表示します。フロントエンドの LLM SDK はありません。キャニスターがクライアントであるため、認証情報がブラウザーに渡ることはありません。 - 呼び出し中は保留状態を表示し、トラップ時には再試行できるようにしてください(アウトコールの往復には数秒かかります)。