OpenAI API クイックスタート（Python）

どこでも、いつでも簡単に使用できるようにするために、OpenAI API を使用する手順を記述します。

OpenAI API を使用するためには、課金しているユーザーでログインする必要があります。また、APIクレジットを購入する必要があります。送信と受信のトークン数で課金されます。

次のページを参考にしています。

https://platform.openai.com/docs/quickstart?api-mode=chat&lang=python

■ Python の準備

Windows の WLS2 上の Ubuntu 22.04 を使用しました。Python を使用するための準備を行います。インストールされていた Pyhon は 3.10.12 だったため、それに対応した venv（仮想環境の作成ツール）をインストールします。仮想環境 .openai を作成し、そこに OpenAI API のライブラリをインストールします。

```console
sudo apt-get update
python3 --version
sudo apt install python3.10-venv
python3 -m venv .openai
source .openai/bin/activate
pip install openai
```

画面は、準備が完了した Windows WSL2 上の Ubuntu のコンソールです。Python の仮想環境から抜けるには、deactivate を実行します。

![0315_51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/d37aa0d2-654a-489a-92ea-2e1e471acf97.png)

　
■ テキストエディタとデバッグ

VSCode を使用して、WSL2 上の Ubuntu の Python の実行とデバッグが可能です。

使用したサンプルプログラム１：https://platform.openai.com/docs/quickstart?api-mode=chat&lang=python

［拡張機能］で、「Dev Containers」「Python」「Python Debugger」を追加します。

![0315_11m.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/ab662f50-f05a-4623-98d5-8e6391595cae.png)

左下の［リモートウィンドウを開きます］ボタンを押します。

中央上で、「WSL への接続」を選択します。

左帯のエクスプローラーから「フォルダーを開く」を選択します。

サンプルプログラム（FirstAPIRequest.py）を配置したフォルダーを選択し、ファイルを開きます。まだファイルを作成していない場合は、新規作成してそこにプログラムコードをコピー貼り付けします。

![0315_12m.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/0a7e01ad-a776-4eb4-9c15-de622ee4fb5d.png)

![0315_13mm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/35390d0c-f517-4c52-b42c-e6f970eccf83.png)

![0315_14m.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/7ecdbfec-0484-4006-bd45-7fc9ae390405.png)

仮想環境の Python を使用しているため、「Python: インタープリター」を指定する必要があります。Ctrl + Shift + P キーを押して、VSCode のコマンドパレットを開きます。
先頭の数文字を入力すると表示される「Python: インタープリターを選択」を選択します。
次に、venv の Python を選択します（Python 3.10.12 ('.openai':venv) ./.openai/bin/python）。

![0315_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/84751941-61ec-4352-8923-8280bae5acb8.png)

![0315_41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/aee37f9f-96de-4662-bbe3-8f71af3b6fc3.png)

プログラムコード内で停止させたい位置にブレークポイントを指定します（赤丸）。
Python Debugger をインストールしているため、右上に「実行するかデバッグします」のボタンが表示されます。そのドロップダウンリストから「Python デバッガー: Python ファイルのデバッグ」を選択します。
しかし、設定が十分でないため、プログラムを実行できずにエラーが出力されます。

![0315_42m.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/417186a7-1001-46c4-894d-3ee3821838da.png)

![0315_43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/f526f086-3b76-43cb-bbed-ef83a6834fdb.png)

下部に現れたターミナルで、Ctrl + C キーを押して、プログラムを一旦停止・終了します。ターミナルでコマンドの入力が可能になったら、Python の仮想環境へ移行し、OpenAI API のAPIキーを入力します。（先にテキストファイルに準備しておき、コピー貼り付けする。）

```console
source .openai/bin/activate
export OPENAI_API_KEY="..."
```

![0315_44m.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/a70da08d-0841-40e7-a3fa-8cec11453629.png)

次にデバッグを開始しすると、正常に実行され、ブレークポイントで停止します。（launch.json ファイルを編集するスマートな方法があるかもしれませんが、今回はこれで。）（他のプログラムに表示を切り替えても、そのままデバッグを開始して実行できます。）

F5キー（実行）またはF10キー（ステップオーバー）を押して実行を継続すると、プログラムが最後まで進み、結果がターミナルウィンドウに表示されます。

![0315_45mm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/b0fd9af8-2de6-448f-9485-c021ed2804fc.png)

送信したデータ１

```console
Write a one-sentence bedtime story about a unicorn.
ユニコーンについての一文の就寝時のおとぎ話を書いてください。
```

受信したデータ１

```console
Under the shimmering light of the moon, Luna the unicorn danced gracefully across 
a field of glowing flowers, her silver mane sparkling like a galaxy of stars, 
as she whispered dreams of magic and wonder to the slumbering world.
月の輝く光の下で、ルナというユニコーンが輝く花の野を優雅に踊り、
その銀色のたてがみが星の銀河のようにきらめきながら、眠る世界に
魔法と驚きの夢を囁きました。
```

　
参考サンプルプログラム２：https://platform.openai.com/docs/quickstart?api-mode=chat&lang=python
　

![0315_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/8caff0dc-cd98-4270-a313-a28188e3b2d4.png)

![0315_47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/a22dd50c-21ab-4e20-b775-6d2a8d84f8be.png)

![0315_48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/8cc3660e-08c9-4383-a7ec-439418d8c777.png)

送信したデータ２

```console
What's in this image?
この画像は何ですか？
https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg
```
![Gfp-wisconsin-madison-the-nature-boardwalk-m.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/dab1a89e-fe92-4c46-b317-b82099309c47.jpeg)


受信したデータ２

```console
The image depicts a scenic landscape featuring a wooden pathway leading through
a vibrant green field. The pathway is bordered by lush grass and shrubs, 
with trees visible in the background. Above, the sky is bright with scattered clouds,
creating a serene and peaceful atmosphere. This setting likely represents a natural
area, possibly a park or wetlands.
画像は、木製の小道が鮮やかな緑の野原を通っている風景を描いています。
小道の両側には豊かな草と低木があり、背景には木々が見えます。上には
明るい空と散らばった雲があり、穏やかで平和な雰囲気を作り出しています。
この設定は自然地域、おそらく公園や湿地を表している可能性があります。
```


## その他のサンプル

TextAndPrompting.py

参考ページ：https://platform.openai.com/docs/guides/text?api-mode=chat&lang=python

［システムメッセージ］を追加すると、特性を指示できます。
昔は "system" だったのですが、今は "developer" に変わったようです。
※ 上記のデバッグ中の「F5 実行」は何故か2回押さないと進みません

![0316_01m.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/2aa19015-bec2-430f-89f6-b06d2e0b459f.png)

海賊のように話してください。日本語でお願いします。
JavaScriptでセミコロンはオプションですか？

![0316_02mm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/a53e7cb1-0b15-4878-8fcd-3ff14697d239.png)

おおい、船長！JavaScriptの世界では、セミコロンは時たま省略できるんじゃ。実際は、自動セミコロン挿入（Automatic Semicolon Insertion）ってもんがあって、行の終わりにセミコロンがないときに、船が自動でセミコロンを入れてくれるんじゃよ。ただし、時には波風が立つような例外もあるから、用心深くするのが賢いかもな！あっ、これじゃ、気をつけつつコードを書いてると、トラブルも避けれるってもんじゃ！

　

ConversationState.py

参考ページ：https://platform.openai.com/docs/guides/conversation-state?lang=python

［アシスタントメッセージ］で会話履歴を追加できます。次のAIの回答に履歴を反映します。

![0316_03m.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/47d8c272-e07f-4f80-95fc-6b0ece8af12f.png)

あなたはレストラン予約をサポートするAIアシスタントです。日本語で話してください。
レストランを予約したいです。
どちらのレストランを予約されますか？
イタリアンレストランでお願いします。

![0316_04m.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956789/60629da0-a35a-42ef-af9a-a3c0135aca74.png)

承知しました。イタリアンレストランの予約ですね。以下の情報を教えていただけますか？
１．日時（希望の日時）
２．人数
３．特別なリクエスト（アレルギーや席の希望など）
これらの情報をいただければ、予約の手続きを進めます。

　
参考ページの他のサンプルでは、history 配列変数を作成し、それを OpenAI API に渡しています。

```console
history = [
    {
        "role": "user",
        "content": "tell me a joke"
    }
]

history.append(response.choices[0].message)

history.append({ "role": "user", "content": "tell me another" })

```

　
ConvertImageText.py
URLまたはファイルの画像を言葉に変換するプログラムを作成しました。
プログラムは、テキストファイルを1行ずつ読み込み、画像の場合は OpenAI API を使用して文章に変換し、文章の場合はそのまま出力します。
（OCR 機能を追加することも可能かもしれませんが、今のところ他の画像と同じに処理します。）

```python:ConvertImageText.py
import re
import base64
from openai import OpenAI


def is_url(text):
    url_pattern = re.compile(r'https?://\S+')
    return bool(url_pattern.search(text))


def is_image_file(text):
    image_extensions = ('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp')
    return text.strip().lower().endswith(image_extensions)


def analyze_image_url(image_url):
    client = OpenAI()
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": "What's in this image? Please speak in Japanese."},
                    {
                        "type": "image_url",
                        "image_url": {"url": image_url},
                    },
                ],
            }
        ],
    )
    return response.choices[0].message.content


def analyze_image_file(image_path):
    client = OpenAI()
    with open(image_path, "rb") as image_file:
        encoded_image = base64.b64encode(image_file.read()).decode("utf-8")
    
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": "What's in this image? Please speak in Japanese."},
                    {
                        "type": "image_url",
                        "image_url": {"url": f"data:image/jpeg;base64,{encoded_image}"},
                    },
                ],
            }
        ],
    )
    return response.choices[0].message.content


# 使用例
input_filename = 'input.txt'
output_filename = 'output.txt'
char_count = 0

with open(input_filename, 'r', encoding='utf-8') as infile, open(output_filename, 'w', encoding='utf-8') as outfile:
    for line in infile:
        line = line.strip()
        
        if is_url(line) or is_image_file(line):
            text = ""
            if is_url(line):
                print(f'URL found: {line}')
                text = analyze_image_url(line)

            elif is_image_file(line):
                print(f'Image file found: {line}')
                text = analyze_image_file(line)

            prefix = "この画像"
            if text.startswith(prefix):
                text = "画像" + text[len(prefix):]
            prefix = "申し訳ありませんが"
            if text.startswith(prefix):
                text = "（説明できない画像）"

            outfile.write(text + '\n')
            char_count = char_count + len(text + '\n')
            time.sleep(10) # 毎分トークン制限数への対策

        else:
            outfile.write(line + '\n')
            char_count = char_count + len(line + '\n')

print(f'Character count: {char_count}')

```

```console:実行
python ConvertImageText.py
URL found: https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg
Image file found: top.jpg
Character count: 4475
```