Models
OpenAI SDK
公式の openai パッケージは、OpenAI の HTTP API を実装したエンドポイントであれば何とでも通信できます。ベース URL とキーを変えるだけで、リトライ、ストリーミング、ページネーション、型付きエラーなど、それ以外はすべてドキュメントどおりに動きます。
インストールと設定
Section titled “インストールと設定”- A Grafilab API key. In the console open API (
https://app.grafilab.ai/api) and click Generate New Key — see API Keys.
キーはソースコードに書かないでください。以下の例では環境変数から読み取ります。
pip install openaiimport osfrom openai import OpenAI
client = OpenAI( api_key=os.environ["GRAFILAB_API_KEY"], base_url="https://llm.grafilab.ai/v1",)npm install openaiimport OpenAI from "openai";
const client = new OpenAI({ apiKey: process.env.GRAFILAB_API_KEY, baseURL: "https://llm.grafilab.ai/v1",});コンストラクタに手を入れたくない場合は — 既存のスクリプトを Grafilab に向けるときに便利です — OPENAI_BASE_URL に https://llm.grafilab.ai/v1 を、OPENAI_API_KEY に Grafilab のキーを設定し、引数なしでクライアントを生成します。ベース URL は /v1 で終わる必要があります。/chat/completions などのパスは SDK が追加します。
モデル ID を選ぶ
Section titled “モデル ID を選ぶ”The model catalog is live, so list it rather than copying ids from a page. Every entry’s id is the exact string to use in any tool or SDK.
curl https://llm.grafilab.ai/v1/models \ -H "Authorization: Bearer $GRAFILAB_API_KEY" | jq -r '.data[].id'from openai import OpenAI
client = OpenAI(api_key="sk-grafilab-...", base_url="https://llm.grafilab.ai/v1")for model in client.models.list(): print(model.id)import OpenAI from "openai";
const client = new OpenAI({ apiKey: "sk-grafilab-...", baseURL: "https://llm.grafilab.ai/v1" });for await (const model of client.models.list()) { console.log(model.id);}The same ids appear as cards in the Playground, with context size and prices.
Chat completions
Section titled “Chat completions”response = client.chat.completions.create( model="grafilab/qwen3.6-flash", messages=[ {"role": "system", "content": "You are terse."}, {"role": "user", "content": "Why is the sky blue?"}, ],)
print(response.choices[0].message.content)print(response.usage)const response = await client.chat.completions.create({ model: "grafilab/qwen3.6-flash", messages: [ { role: "system", content: "You are terse." }, { role: "user", content: "Why is the sky blue?" }, ],});
console.log(response.choices[0].message.content);console.log(response.usage);model、messages、stream、stream_options 以外のフィールドは、パラメーターとしてそのままモデルへ渡されます。つまり temperature、top_p、max_tokens、tools、response_format は、下位のモデルが対応している限り機能し、未知のパラメーターは Grafilab ではなくモデルによって無視されるか拒否されます。テキストは配信後に、1 リクエストにつき 1 トランザクションで課金されます。
ストリーミング
Section titled “ストリーミング”使用量は明示的に要求してください。stream_options を付けないと、ストリーミング応答にはトークン数がまったく含まれません。
stream = client.chat.completions.create( model="grafilab/qwen3.6-flash", messages=[{"role": "user", "content": "Count to five."}], stream=True, stream_options={"include_usage": True},)
for chunk in stream: if chunk.usage: print("\nusage:", chunk.usage) elif chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="")const stream = await client.chat.completions.create({ model: "grafilab/qwen3.6-flash", messages: [{ role: "user", content: "Count to five." }], stream: true, stream_options: { include_usage: true },});
for await (const chunk of stream) { if (chunk.usage) { console.log("\nusage:", chunk.usage); } else if (chunk.choices[0]?.delta?.content) { process.stdout.write(chunk.choices[0].delta.content); }}使用量を運ぶチャンクは最後、data: [DONE] の終端の直前に届き、その choices 配列は空です。
推論トレースはメッセージ上の reasoning と reasoning_content の両方に、同じ内容で返ります。思考をインラインのタグとして出力するモデルも同じ 2 つのフィールドに正規化されるので、表示テキストから切り出す必要はありません。どちらのフィールドも OpenAI の型定義には含まれないため、生のオブジェクトから読み取ってください。
モデル一覧を取得する
Section titled “モデル一覧を取得する”for model in client.models.list(): print(model.id, getattr(model, "region", None), getattr(model, "tags", None))for await (const model of client.models.list()) { console.log(model.id, (model as any).region, (model as any).tags);}すべての id は Grafilab のエイリアスであり、model として送るべき正確な文字列です。エイリアスは大文字小文字を区別せずに一致し、/ を含むことがあります。またレスポンスは正規の綴りを返すため、送った文字列と異なる場合があります。標準の OpenAI フィールドに加えて、各エントリは Grafilab の拡張である region と tags を持ちます。
エラーを処理する
Section titled “エラーを処理する”どちらの SDK もステータスコードごとに型付きの例外を投げます。Grafilab はそのうち 2 つを、OpenAI のセマンティクスを前提にしていると驚くような使い方をしています。
| 例外 | ステータス | Grafilab での意味 |
|---|---|---|
AuthenticationError | 401 | キーが未知、失効済み、または不正な形式です。キーごとの IP 許可リストが呼び出し元を除外している場合にも発生します |
PermissionDeniedError | 403 | billing_currency_unset — アカウントに請求通貨が設定されていません。プロフィールで国を設定してください |
NotFoundError | 404 | model_not_found。エイリアスが誤っているか、モデルは存在するがあなたの通貨で価格が設定されていません |
RateLimitError | 429 | スループットではなく残高不足です。 バックオフではなくチャージしてください |
APIStatusError | 500 | インフラ障害です。ゲートウェイのエラーは 500 として返され、502 や 504 にはなりません |
500 は、レスポンスに x-should-retry: false が付いていない限り再試行して問題ありません。この値は処理がすでに完了して課金済みであることを示します。すべてのレスポンスには X-Request-Id ヘッダー(req_ に続く 16 進 24 文字)が付くので、サポートへの問い合わせではこの値を伝えてください。
import openai
try: raw = client.chat.completions.with_raw_response.create( model="grafilab/qwen3.6-flash", messages=[{"role": "user", "content": "Hello"}], ) print(raw.headers.get("x-request-id")) completion = raw.parse()except openai.RateLimitError: print("Top up your Grafilab credit balance.")except openai.APIStatusError as err: print(err.status_code, err.response.headers.get("x-should-retry"), err.response.text)try { const { data, response } = await client.chat.completions .create({ model: "grafilab/qwen3.6-flash", messages: [{ role: "user", content: "Hello" }], }) .withResponse(); console.log(response.headers.get("x-request-id"), data.choices[0].message.content);} catch (err) { if (err instanceof OpenAI.RateLimitError) { console.log("Top up your Grafilab credit balance."); } else if (err instanceof OpenAI.APIError) { console.log(err.status, err.headers?.["x-should-retry"], err.message); }}埋め込みと画像
Section titled “埋め込みと画像”encoding_format は float(JSON 配列)または base64(リトルエンディアンの float32 で、転送量はおよそ 3 分の 1)を受け付けます。テキストと同様、埋め込みも配信後に課金されます。
result = client.embeddings.create( model="<embedding-model-id>", input=["the first document", "the second document"], encoding_format="float",)print(len(result.data[0].embedding))const result = await client.embeddings.create({ model: "<embedding-model-id>", input: ["the first document", "the second document"], encoding_format: "float",});console.log(result.data[0].embedding.length);images.generate は同期的です。先に生成し、同じトランザクションで課金するため、失敗した場合の費用はかかりません。response_format は url か b64_json で、n は 1 から 10 を受け付けます。
image = client.images.generate( model="<image-model-id>", prompt="a lighthouse in fog, long exposure", n=1, response_format="url",)print(image.data[0].url)const image = await client.images.generate({ model: "<image-model-id>", prompt: "a lighthouse in fog, long exposure", n: 1, response_format: "url",});console.log(image.data[0].url);images.edit はマルチパートのフォームデータをアップロードし、編集に対応したモデルを必要とします。それ以外のモデルではコード edit_unsupported の 400 が返ります。1 ファイルあたり 20 MB、1 リクエストあたり 10 ファイルが上限です。
edited = client.images.edit( model="<edit-capable-model-id>", image=open("source.png", "rb"), prompt="replace the sky with a clear night sky",)import fs from "node:fs";import { toFile } from "openai";
const edited = await client.images.edit({ model: "<edit-capable-model-id>", image: await toFile(fs.createReadStream("source.png")), prompt: "replace the sky with a clear night sky",});動画は非同期です。ジョブを作成し、ポーリングし、結果をダウンロードします。ジョブは queued → in_progress → completed または failed と進みます。状態を進めるのはポーリングそのものなので、終端の状態に達するまでポーリングを続けてください。
動画は作成時に、送信のトランザクション内で課金され、その後ジョブが失敗した場合は自動的に返金されます。完成した結果は完了から 7 日後に期限切れになるので、残したいものはダウンロードしてください。
import time
video = client.videos.create(model="<video-model-id>", prompt="a paper boat crossing a puddle")
while video.status in ("queued", "in_progress"): time.sleep(5) video = client.videos.retrieve(video.id)
if video.status == "failed": raise RuntimeError(video.error)
content = client.videos.download_content(video.id)content.write_to_file("out.mp4")let video = await client.videos.create({ model: "<video-model-id>", prompt: "a paper boat crossing a puddle",});
while (video.status === "queued" || video.status === "in_progress") { await new Promise((r) => setTimeout(r, 5000)); video = await client.videos.retrieve(video.id);}
if (video.status === "failed") throw new Error(String(video.error));
const content = await client.videos.downloadContent(video.id);// content is a Response — stream or buffer its body to a file.GET /v1/videos/{video_id}/content は、キーなしで誰でも読める MP4 の URL への 302 を返します。ブラウザにリンクを渡すには便利ですが、それはログに残すべきでない理由でもあります。Grafilab が実装しているのは作成、一覧、取得、コンテンツのダウンロードです。SDK の remix、edit、extend、delete の各メソッドに対応するものはなく、remixed_from_video_id は常に null です。

