コンテンツにスキップ

Models

OpenAI SDK

公式の openai パッケージは、OpenAI の HTTP API を実装したエンドポイントであれば何とでも通信できます。ベース URL とキーを変えるだけで、リトライ、ストリーミング、ページネーション、型付きエラーなど、それ以外はすべてドキュメントどおりに動きます。

  • A Grafilab API key. In the console open API (https://app.grafilab.ai/api) and click Generate New Key — see API Keys.

キーはソースコードに書かないでください。以下の例では環境変数から読み取ります。

ターミナルウィンドウ
pip install openai
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["GRAFILAB_API_KEY"],
base_url="https://llm.grafilab.ai/v1",
)

コンストラクタに手を入れたくない場合は — 既存のスクリプトを Grafilab に向けるときに便利です — OPENAI_BASE_URL に https://llm.grafilab.ai/v1 を、OPENAI_API_KEY に Grafilab のキーを設定し、引数なしでクライアントを生成します。ベース URL は /v1 で終わる必要があります。/chat/completions などのパスは SDK が追加します。

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.

Terminal window
curl https://llm.grafilab.ai/v1/models \
-H "Authorization: Bearer $GRAFILAB_API_KEY" | jq -r '.data[].id'

The same ids appear as cards in the Playground, with context size and prices.

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)

model、messages、stream、stream_options 以外のフィールドは、パラメーターとしてそのままモデルへ渡されます。つまり temperature、top_p、max_tokens、tools、response_format は、下位のモデルが対応している限り機能し、未知のパラメーターは Grafilab ではなくモデルによって無視されるか拒否されます。テキストは配信後に、1 リクエストにつき 1 トランザクションで課金されます。

使用量は明示的に要求してください。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="")

使用量を運ぶチャンクは最後、data: [DONE] の終端の直前に届き、その choices 配列は空です。

推論トレースはメッセージ上の reasoning と reasoning_content の両方に、同じ内容で返ります。思考をインラインのタグとして出力するモデルも同じ 2 つのフィールドに正規化されるので、表示テキストから切り出す必要はありません。どちらのフィールドも OpenAI の型定義には含まれないため、生のオブジェクトから読み取ってください。

for model in client.models.list():
print(model.id, getattr(model, "region", None), getattr(model, "tags", None))

すべての id は Grafilab のエイリアスであり、model として送るべき正確な文字列です。エイリアスは大文字小文字を区別せずに一致し、/ を含むことがあります。またレスポンスは正規の綴りを返すため、送った文字列と異なる場合があります。標準の OpenAI フィールドに加えて、各エントリは Grafilab の拡張である region と tags を持ちます。

どちらの SDK もステータスコードごとに型付きの例外を投げます。Grafilab はそのうち 2 つを、OpenAI のセマンティクスを前提にしていると驚くような使い方をしています。

例外ステータスGrafilab での意味
AuthenticationError401キーが未知、失効済み、または不正な形式です。キーごとの IP 許可リストが呼び出し元を除外している場合にも発生します
PermissionDeniedError403billing_currency_unset — アカウントに請求通貨が設定されていません。プロフィールで国を設定してください
NotFoundError404model_not_found。エイリアスが誤っているか、モデルは存在するがあなたの通貨で価格が設定されていません
RateLimitError429スループットではなく残高不足です。 バックオフではなくチャージしてください
APIStatusError500インフラ障害です。ゲートウェイのエラーは 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)

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))

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)

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",
)

動画は非同期です。ジョブを作成し、ポーリングし、結果をダウンロードします。ジョブは 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")

GET /v1/videos/{video_id}/content は、キーなしで誰でも読める MP4 の URL への 302 を返します。ブラウザにリンクを渡すには便利ですが、それはログに残すべきでない理由でもあります。Grafilab が実装しているのは作成、一覧、取得、コンテンツのダウンロードです。SDK の remix、edit、extend、delete の各メソッドに対応するものはなく、remixed_from_video_id は常に null です。