🙆‍♀️

OpenAI Python SDK v1.0がめちゃくちゃ使いやすくなってた

2024/03/01に公開2

ハイライト

  • インスタンス化されたクライアントの利用を推奨するようになった
  • Async/Azure専用のクライアントが追加された
  • レスポンス(出力)がPydantic Modelになった
  • リクエストパラメータ(入力)がTypedDictになった

インスタンス化されたクライアントの利用を推奨するようになった

これまではopenaiモジュールに定義されていたopenai.ChatCompletion.create関数をよんでいたところがインスタンス化されたOpenAIオブジェクトのchat.completions.createメソッドを呼ぶ仕様になりました。

これまで

import json
import openai

completion = openai.ChatCompletion.create(model='curie')
print(completion['choices'][0]['text'])
print(completion.get('usage'))
print(json.dumps(completion, indent=2))

これから

from openai import OpenAI

client = OpenAI(
    api_key=os.environ['OPENAI_API_KEY'],  # デフォルト値なので省略してよい
omitted)

completion = client.chat.completions.create(model='curie')
print(completion.choices[0].text)
print(dict(completion).get('usage'))
print(completion.model_dump_json(indent=2))

これがどううれしいかというと、これまで環境変数の読み込みのタイミングはopenaiモジュールに隠された内部実装でした。ユーザーはopenai.ChatCompletion.createを呼ぶ前に環境変数を設定する必要があります。新実装ではOpenAI()を呼ぶタイミングで環境変数がセットされているか、任意の文字列を入力することでこの目的を達成できます。

Async/Azure専用のクライアントが追加された

この新しいクライアントは

  1. Asyncか否か
  2. Azureか否か
    で全部で4バージョン(OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI)あります。
    AsyncClientが登場したことでopenaiモジュールに定義されたopenai.ChatCompletion.acreate関数は利用できなくなりました。また間違えやすかったエンドポイント周りの設定ミスが明示的にクライアントが分かれたことでわかりやすくなりましたね。
client = AsyncAzureOpenAI(  
      api_key = os.getenv("AZURE_OPENAI_API_KEY"),  
      api_version = "2023-12-01-preview",
      azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT")
    )
    response = await client.chat.completions.create(model="gpt-35-turbo", messages=[{"role": "user", "content": "Hello world"}])

※そもそもOpenAIのリクエストを非同期並列でリクエストできるって知っていましたか?詳しく書いている素敵な記事を見つけたのでぜひご覧ください。

レスポンス(出力)がPydantic Modelになった

最近のPythonコミュニティはType Safeなソースコードが大好きです。余談ですがOpenAIはWhisperをリリースしたときもType Safeなクリーンなコードを公開しています。Whisperではdataclassが使われていましたがOpenAI SDK v1.0からはPydanticを採用しdataclassよりもさらにクリーンでJSONへの変換が容易になっています。

これまで

import json
import openai

completion = openai.Completion.create(model='curie')
print(completion['choices'][0]['text'])  # 汚いstrリテラルでのアクセス
print(completion.get('usage'))
print(json.dumps(completion, indent=2))

これから

from openai import OpenAI

client = OpenAI()

completion = client.completions.create(model='curie')
print(completion.choices[0].text)  # クリーンなドットアクセスでvscodeもニッコリ
print(dict(completion).get('usage'))
print(completion.model_dump_json(indent=2))

リクエストパラメータ(入力)がTypedDictになった

入力に渡していたちょっとめんどくさい辞書のリストがTypedDictになりました。キーやバリューの許容値がLiteralで制限されているのでかなり厳しくエラーをチェックしてくれます。

from openai import OpenAI

client = OpenAI()
completion = client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=[
        {
            "role": "ssystem",
            "content": "You are a helpful assistant.",
        },  # TypedDictがsystemを要求しているので、ssystemはエラーになる
        {
            "role": "user",
            "contant": "What is human life expectancy in the United States?",  # contantはエラーになる
        },
    ],
)
response = completion.choices

当然の疑問としては出力がPydanticModelなんだから入力もPydanticModelに統一したほうがいいんじゃないの?ってところですよね。そこはver0からの破壊的な変更を嫌ったのでしょうか。でもTypedDictで十分エディターの恩恵を得られるので、超ナイスアップデートですね!

まとめ

OpenAIのソースコードめちゃ綺麗になりましたね。もともとOpenAIってすごい研究者を集めてはいるものの研究としての新規性というより、既存の研究を大規模にスケールさせるエンジニアリングで著名な成果を残している印象です。リリースから数カ月たって今更解説記事書いちゃいましたが、よかったらいいねおしていただけるととてもとても励みになります。

Discussion

まちゅけんまちゅけん

OpenAI Python SDK v1.0 からは Stainless という SDK 自動生成のフレームワークが利用されているようです。

https://www.stainlessapi.com/
https://www.stainlessapi.com/customers/openai

(Pull request テンプレートに自動生成ということが言及されている)

https://github.com/openai/openai-python/blob/v1.14.3/.github/pull_request_template.md

Stainless になったことでこの記事の言及の通り素晴らしい API になっていて驚いています ✨
いつも良質な記事ありがとうございます!

YosematYosemat

Stainless知りませんでした。なぜこれまでだれも作らなかったのかと思わされるような素敵なツールですね。

awsのboto3のような自動実装系のライブラリはみんなこうした仕組みでクリーンに再実装されて欲しいです!