Closed14

ターミナルでAIエージェントが操作してくれる「gptme」を試す

kun432kun432

ここで知った。

https://twitter.com/rohanpaul_ai/status/1841999030999470326

GitHubレポジトリ
https://github.com/ErikBjare/gptme

gptme

📜 ターミナル内でLLMアシスタントとチャット形式のインターフェースで直接やりとりできる。ツールにより、アシスタントはシェルコマンドの実行、コードの実行、ファイルの読み書きなどを行うことができ、あらゆる種類の開発やターミナルベースの作業をアシストできる。

ソフトウェアの不足、インターネットアクセス、タイムアウト、プライバシーの懸念(ローカルモデルを使用する場合)に制約されない、ChatGPTの「コードインタープリター」のローカル代替手段。

🌟 機能

  • 💻 コードの実行
    • シェルPythonツールを使用して、ローカル環境でコードを実行する。
  • 🧩 ファイルの読み取り、書き込み、変更
    • パッチツールを使用して、インクリメンタルな変更を行う。
  • 🌐 Webの検索と閲覧
    • ブラウザツールを使用して、Playwright経由でブラウザを使用できる。
  • 👀 ビジョン
    • プロンプトで参照された画像、デスクトップのスクリーンショット、Webページを見ることができる。
  • 🔄 自己修正
    • アシスタントにフィードバックすることで、応答と自己修正が可能になる。
  • 🤖 複数のLLMプロバイダーをサポート
    • OpenAI、Anthropic、OpenRouter、またはllama.cppでローカルに提供する
  • ✨ 素晴らしい体験を保証する多くの小さな機能
    • 🚰 stdinまたは引数としてコンテキストをパイプで接続する。
      • ファイル名を引数として渡すと、そのファイルが読み込まれ、コンテキストとして含まれる。
    • → タブ補完
    • 📝 会話の自動命名
    • 💬 オプションの簡易Web UIとREST API

🛠 開発者向けの特典

  • 🧰 拡張が容易
    • ほとんどの機能がツールとして実装されているため、新しい機能を追加しやすい。
  • 🧪 広範なテスト、高い網羅率
  • 🧹 きれいなコードベース、mypyruffpyupgradeでチェックおよびフォーマット済み
  • 🤖 GitHub Botでコメントからの変更をリクエスト! (#16を参照)
    • このリポジトリで動作する! (例については #18 を参照)
    • GitHub Actions で完全に実行される。
  • 📊 異なるモデルの能力をテストするための評価スイート

🚧 進行中

  • 🏆 最先端の評価によるフロンティア機能のテスト
  • 🤖 長時間稼働するエージェントと高度なエージェントアーキテクチャ
  • 🌳 ツリーベースの会話構造(#17を参照)

🛠 ユースケース

  • 🎯 Shell Copilot: 自然言語を使用して適切なシェルコマンドを特定(もうフラグを暗記する必要なし!)。
  • 🖥 開発: AIの支援を受けながらコードを記述、テスト、実行。
  • 📊 データ分析: ローカルファイルのデータ分析や操作を簡単に実行できる。
  • 🎓 学習とプロトタイピング: 新しいライブラリやフレームワークを即座に試すことができる。
  • 🤖 エージェントとツール: ローカル環境でエージェントとツールを試すことができる。
kun432kun432

とりあえずDocker上で軽くお試し。

$ docker run -it --rm python:3.12-slim-bookworm bash

コンテナに入ったら、パッケージインストール

$ pip install gptme

Usageを確認

$ gptme --help
Usage: gptme [OPTIONS] [PROMPTS]...

  gptme is a chat-CLI for LLMs, empowering them with tools to run shell
  commands, execute code, read and manipulate files, and more.

  If PROMPTS are provided, a new conversation will be started with it. PROMPTS
  can be chained with the '-' separator.

  The interface provides user commands that can be used to interact with the
  system.

  Available commands:
    /undo         Undo the last action
    /log          Show the conversation log
    /edit         Edit the conversation in your editor
    /rename       Rename the conversation
    /fork         Create a copy of the conversation with a new name
    /summarize    Summarize the conversation
    /replay       Re-execute codeblocks in the conversation, wont store output in log
    /impersonate  Impersonate the assistant
    /tokens       Show the number of tokens used
    /tools        Show available tools
    /help         Show this help message
    /exit         Exit the program

Options:
  -n, --name TEXT        Name of conversation. Defaults to generating a random
                         name.
  -m, --model TEXT       Model to use, e.g. openai/gpt-4o,
                         anthropic/claude-3-5-sonnet-20240620. If only
                         provider given, a default is used.
  -w, --workspace TEXT   Path to workspace directory. Pass '@log' to create a
                         workspace in the log directory.
  -r, --resume           Load last conversation
  -y, --no-confirm       Skips all confirmation prompts.
  -n, --non-interactive  Force non-interactive mode. Implies --no-confirm.
  --system TEXT          System prompt. Can be 'full', 'short', or something
                         custom.
  -t, --tools TEXT       Comma-separated list of tools to allow. Available:
                         read, save, append, patch, shell, subagent, chats,
                         youtube, screenshot, vision, python.
  --no-stream            Don't stream responses
  --show-hidden          Show hidden system messages.
  -v, --verbose          Show verbose output.
  --version              Show version and configuration information
  --help                 Show this message and exit.

サラッと実行してみる

$ gptme
FileNotFoundError: [Errno 2] No such file or directory: 'git'

どうやらgitは必須みたいなので、インストール

$ apt update
$ apt install -y git

再度実行

$ gptme

以下のような画面が表示されるので、"New conversation"を選択

New conversation or load previous?

* New conversation
  Load more

APIキーを求められるので入力。自分はOpenAI APIキーにした。

No API key set for OpenAI, Anthropic, or OpenRouter.
You can get one at:
 - OpenAI: https://platform.openai.com/account/api-keys
 - Anthropic: https://console.anthropic.com/settings/keys
 - OpenRouter: https://openrouter.ai/settings/keys

Your OpenAI or Anthropic API key:

ホームディレクトリ以下に設定が保存されて、対話モードになる。

Your OpenAI or Anthropic API key: sk-XXXXXXXXXXXX
API key saved to config at /root/.config/gptme/config.toml
[14:25:41] No model specified, using recommended model for provider: gpt-4o
           Using logdir ~/.local/share/gptme/logs/2024-10-07-jumping-hungry-octopus
           Using workspace at /
Skipped 1 hidden system messages, show with --show-hidden
--- ^^^ past messages ^^^ ---
User:

対話モードのまま、少しGetting Startedの内容を確認してみる。

https://gptme.org/docs/getting-started.html

User: life.pyという名前の新しいファイルを作成して、ライフゲームを実装して。
Assistant: ライフゲーム(Conway's Game of Life)は、セルオートマトンの一種で、シンプルなルールに基づいてセルの生死をシミュレートします。以下に、基本的なライフゲームの実装を示します。

```save life.py
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

# グリッドのサイズ
GRID_SIZE = 50

def initialize_grid(size):
    """ランダムに初期化されたグリッドを生成します。"""
    return np.random.choice([0, 1], size*size, p=[0.8, 0.2]).reshape(size, size)

def update(frameNum, img, grid, size):
    """グリッドを更新して次の世代を生成します。"""
    newGrid = grid.copy()
    for i in range(size):
        for j in range(size):
            # 近傍のセルの合計を計算
            total = int((grid[i, (j-1)%size] + grid[i, (j+1)%size] +
                         grid[(i-1)%size, j] + grid[(i+1)%size, j] +
                         grid[(i-1)%size, (j-1)%size] + grid[(i-1)%size, (j+1)%size] +
                         grid[(i+1)%size, (j-1)%size] + grid[(i+1)%size, (j+1)%size]))

            # ライフゲームのルールに基づいてセルの状態を更新
            if grid[i, j] == 1:
                if (total < 2) or (total > 3):
                    newGrid[i, j] = 0
            else:
                if total == 3:
                    newGrid[i, j] = 1

    # グリッドを更新
    img.set_data(newGrid)
    grid[:] = newGrid[:]
    return img,

def main():
    # 初期グリッドを生成
    grid = initialize_grid(GRID_SIZE)

    # アニメーションの設定
    fig, ax = plt.subplots()
    img = ax.imshow(grid, interpolation='nearest')
    ani = animation.FuncAnimation(fig, update, fargs=(img, grid, GRID_SIZE),
                                  frames=10, interval=200, save_count=50)

    plt.show()

if __name__ == '__main__':
    main()
```
 Save to life.py? [Y/n]

コードが生成されるとともに、確認が行われる様子。"Y"ですすめる。

 Save to life.py? [Y/n] Y
Saving to life.py
System: Saved to life.py
Assistant: `life.py`にライフゲームの基本的な実装を保存しました。このスクリプトは、ランダムに初期化されたグリッドを生成し、ライフゲームのルールに従ってセルの状態を更新します。アニメーションを使用して、各世代の変化を視覚的に表示します。

このスクリプトを実行するには、Python環境に`numpy`と`matplotlib`がインストールされている必要があります。これらのライブラリがインストールされていない場合は、以下のコマンドを使用してインストールできます。

```bash
pip install numpy matplotlib
```

Preview
$ pip install numpy matplotlib
```

 Execute code? [Y/n]

更にパッケージインストールコマンドの実行の確認が行われる。おそらくコマンドの実行やファイルの書き込みなどが行われる場合はユーザに確認するようになっているのだと思う。このまま続けてみる。

 Execute code? [Y/n]  Y
Collecting numpy
  Downloading numpy-2.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (62 kB)
Collecting matplotlib
  Downloading matplotlib-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (11 kB)
(snip)
Successfully installed contourpy-1.3.0 cycler-0.12.1 fonttools-4.54.1 kiwisolver-1.4.7 matplotlib-3.9.2 numpy-2.1.2 pillow-10.4.0 pyparsing-3.1.4 python-dateutil-2.9.0.post0
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager, possibly rendering your system unusable.It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv. Use the --root-user-action option if you know what you are doing and want to suppress this warning.

System:
Ran command: `pip install numpy matplotlib`

stdout:
```
Collecting numpy
  Downloading numpy-2.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (62 kB)
Collecting matplotlib
  Downloading matplotlib-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (11 kB)
Collecting contourpy>=1.0.1 (from matplotlib)
  Downloading contourpy-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (5.4 kB)
Collecting cycler>=0.10 (from matplotlib)
  Downloading cycler-0.12.1-py3-none-any.whl.metadata (3.8 kB)
Collecting fonttools>=4.22.0 (from matplotlib)
  Downloading fonttools-4.54.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (163 kB)
Collecting kiwisolver>=1.3.1 (from matplotlib)
  Downloading kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (6.3 kB)
Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.12/site-packages (from matplotlib) (24.1)
Collecting pillow>=8 (from matplotlib)
  Downloading pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl.metadata (9.2 kB)
Collecting pyparsing>=2.3.1 (from matplotlib)
  Downloading pyparsing-3.1.4-py3-none-any.whl.metadata (5.1 kB)
Collecting python-dateutil>=2.7 (from matplotlib)
  Downloading python_dateutil-2.9.0.post0-py2.py3-none-any.whl.metadata (8.4 kB)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/site-packages (from python-dateutil>=2.7->matplotlib)
Downloading numpy-2.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (13.6 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 13.6/13.6 MB 9.3 MB/s eta 0:00:00
Downloading matplotlib-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (8.2 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 8.2/8.2 MB 9.7 MB/s eta 0:00:00
Downloading contourpy-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (303 kB)
Downloading cycler-0.12.1-py3-none-any.whl (8.3 kB)
Downloading fonttools-4.54.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (4.8 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.8/4.8 MB 9.8 MB/s eta 0:00:00
Downloading kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.5 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.5/1.5 MB 8.5 MB/s eta 0:00:00
Downloading pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl (4.4 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.4/4.4 MB 10.0 MB/s eta 0:00:00
Downloading pyparsing-3.1.4-py3-none-any.whl (104 kB)
Downloading python_dateutil-2.9.0.post0-py2.py3-none-any.whl (229 kB)
Installing collected packages: python-dateutil, pyparsing, pillow, numpy, kiwisolver, fonttools, cycler, contourpy, matplo
Successfully installed contourpy-1.3.0 cycler-0.12.1 fonttools-4.54.1 kiwisolver-1.4.7 matplotlib-3.9.2 numpy-2.1.2 pillow
```

stderr: `WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the
system package manager, possibly rendering your system unusable.It is recommended to use a virtual environment instead:
https://pip.pypa.io/warnings/venv. Use the --root-user-action option if you know what you are doing and want to suppress
this warning.`
Assistant: `numpy`と`matplotlib`のインストールが完了しました。これで、`life.py`を実行してライフゲームを観察することができます。以下のコマンドを使用してスクリプトを実行してください。

```bash
python life.py
```
Preview
$ python life.py

 Execute code? [Y/n]

なるほど、実行結果等の出力もLLMに渡されている。

 Execute code? [Y/n] Y
//life.py:43: UserWarning: You passed in an explicit save_count=50 which is being ignored in favor of frames=10.
  ani = animation.FuncAnimation(fig, update, fargs=(img, grid, GRID_SIZE),
/usr/local/lib/python3.12/site-packages/matplotlib/animation.py:872: UserWarning: Animation was deleted without rendering anything. This is most likely not intended. To prevent deletion, assign the Animation to a variable, e.g. `anim`, that exists until you output the Animation using `plt.show()` or `anim.save()`.
  warnings.warn(

System:
Ran command: `python life.py`

stderr:
```
//life.py:43: UserWarning: You passed in an explicit save_count=50 which is being ignored in favor of frames=10.
  ani = animation.FuncAnimation(fig, update, fargs=(img, grid, GRID_SIZE),
/usr/local/lib/python3.12/site-packages/matplotlib/animation.py:872: UserWarning: Animation was deleted without rendering
  warnings.warn(
```
Assistant: エラーメッセージによると、アニメーションが正しく表示されていないようです。`FuncAnimation`の設定に問題がある可能性があります。`save_count`の設定が`frames`の設定と競合しているため、アニメーションが削除されてしまっているようです。

この問題を修正するために、`save_count`を削除し、`frames`の設定を適切に行います。以下のようにコードを修正します。

```patch life.py
<<<<<<< ORIGINAL
    ani = animation.FuncAnimation(fig, update, fargs=(img, grid, GRID_SIZE),
                                  frames=10, interval=200, save_count=50)
=======
    ani = animation.FuncAnimation(fig, update, fargs=(img, grid, GRID_SIZE),
                                  frames=200, interval=200)
>>>>>>> UPDATED
```
Preview
     ani = animation.FuncAnimation(fig, update, fargs=(img, grid, GRID_SIZE),
-                                  frames=10, interval=200, save_count=50)
+                                  frames=200, interval=200)

 Apply patch to life.py? [Y/n]

実行してみたがエラーということで、このエラー出力を元に、さらにファイルへの修正がpatchとして行われる、あー、イチから生成し直すんじゃなくて差分でpatchしてくれるのは良いね!

 Apply patch to life.py? [Y/n] Y
System: Patch successfully applied to life.py
Assistant: 修正を適用しました。これでアニメーションが正しく表示されるはずです。再度、`life.py`を実行してライフゲームを観察してみてください。

```bash
python life.py
```
Preview
$ python life.py

 Execute code? [Y/n]
 Execute code? [Y/n] Y
/usr/local/lib/python3.12/site-packages/matplotlib/animation.py:872: UserWarning: Animation was deleted without rendering anything. This is most likely not intended. To prevent deletion, assign the Animation to a variable, e.g. `anim`, that exists until you output the Animation using `plt.show()` or `anim.save()`.
  warnings.warn(

System:
Ran command: `python life.py`

stderr:
```
/usr/local/lib/python3.12/site-packages/matplotlib/animation.py:872: UserWarning: Animation was deleted without rendering
  warnings.warn(
```
Assistant: アニメーションが削除されてしまう問題がまだ解決されていないようです。この問題は、アニメーションオブジェクトがガベージコレクションによって削除されてしまうことが原因です。アニメーションオブジェクトを変数に保持することで、この問題を解決できます。

以下のようにコードを修正します。

```patch life.py
<<<<<<< ORIGINAL
    ani = animation.FuncAnimation(fig, update, fargs=(img, grid, GRID_SIZE),
                                  frames=200, interval=200)
=======
    anim = animation.FuncAnimation(fig, update, fargs=(img, grid, GRID_SIZE),
                                   frames=200, interval=200)
>>>>>>> UPDATED
```
Preview
-    ani = animation.FuncAnimation(fig, update, fargs=(img, grid, GRID_SIZE),
-                                  frames=200, interval=200)
+    anim = animation.FuncAnimation(fig, update, fargs=(img, grid, GRID_SIZE),
+                                   frames=200, interval=200)

 Apply patch to life.py? [Y/n]

こんな感じで続けることができる。

なお、今回の例では、差分だけ変更では完成せず、また、ファイルを全部書き換えたりもしてたんだけど、最終的に完成するところまで行けなさそうだったので、途中でnを入力したら、対話モードに戻ってきた。

 Apply patch to life.py? [Y/n]  n
Patch not applied
User: 

対話モードから抜けるときは/exit

User: /exit
kun432kun432

設定ファイルはこんな感じになっていた。

$ cat ~/.config/gptme/config.toml
~/.config/gptme/config.toml
[prompt]
about_user = "I am a curious human programmer."
response_preference = "Basic concepts don't need to be explained."

[prompt.project]
activitywatch = "ActivityWatch is a free and open-source automated time-tracker that helps you track how you spend your time on your devices."
gptme = "gptme is a CLI to interact with large language models in a Chat-style interface, enabling the assistant to execute commands and code on the local machine, letting them assist in all kinds of development and terminal-based work."

[env]
OPENAI_API_KEY = "sk-XXXXXXXXXXXX"
kun432kun432

Getting Startedを見てみると以下のような記載がある。

ブラウザ拡張機能(extras)をインストールすると、アシスタントはプロンプトに含まれるURLも処理できる。

https://github.com/ErikBjare/gptme/blob/master/pyproject.toml#L80-L91

なるほど。ブラウザだけでなく、WebGUIのサーバとかもある。であればallでインストールすればフルで使えそう。

$ pip install -U gptme[all]
kun432kun432

今度はコマンドラインで使ってみる。examplesの例を参考に。

https://gptme.org/docs/examples.html

$ gptme "three.jsを使用して、印象的でカラフルなパーティクル効果を表示するWebアプリをparticles.htmlとして作成して"
Found OpenAI API key, using OpenAI provider
[15:16:06] No model specified, using recommended model for provider: gpt-4o
           Using logdir ~/.local/share/gptme/logs/2024-10-07-sprinting-pink-walrus
           Using workspace at /
Skipped 1 hidden system messages, show with --show-hidden
--- ^^^ past messages ^^^ ---
User: three.jsを使用して、印象的でカラフルなパーティクル効果を表示するWebアプリをparticles.htmlとして作成して
Assistant: Three.jsを使用してカラフルなパーティクル効果を表示するWebアプリを作成するには、以下の手順に従います。まず、基本的なHTMLファイルを作成し、Three.jsを使用してパーティクルシステムを設定します。

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Colorful Particle Effect</title>
    <style>
        body { margin: 0; overflow: hidden; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        // Scene setup
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // Particle system
        const particles = new THREE.BufferGeometry();
        const particleCount = 1000;
        const positions = new Float32Array(particleCount * 3);
        const colors = new Float32Array(particleCount * 3);

        for (let i = 0; i < particleCount; i++) {
            positions[i * 3] = (Math.random() - 0.5) * 10;
            positions[i * 3 + 1] = (Math.random() - 0.5) * 10;
            positions[i * 3 + 2] = (Math.random() - 0.5) * 10;

            colors[i * 3] = Math.random();
            colors[i * 3 + 1] = Math.random();
            colors[i * 3 + 2] = Math.random();
        }

        particles.setAttribute('position', new THREE.BufferAttribute(positions, 3));
        particles.setAttribute('color', new THREE.BufferAttribute(colors, 3));

        const particleMaterial = new THREE.PointsMaterial({
            size: 0.1,
            vertexColors: true,
            transparent: true,
            opacity: 0.8
        });

        const particleSystem = new THREE.Points(particles, particleMaterial);
        scene.add(particleSystem);

        camera.position.z = 5;

        // Animation loop
        function animate() {
            requestAnimationFrame(animate);

            particleSystem.rotation.x += 0.001;
            particleSystem.rotation.y += 0.001;

            renderer.render(scene, camera);
        }

        animate();

        // Handle window resize
        window.addEventListener('resize', () => {
            const width = window.innerWidth;
            const height = window.innerHeight;
            renderer.setSize(width, height);
            camera.aspect = width / height;
            camera.updateProjectionMatrix();
        });
    </script>
</body>
</html>
```

このコードは、Three.jsを使用してカラフルなパーティクルシステムを作成し、アニメーション化します。パーティクルはランダムな位置と色を持ち、シーン内で回転します。ウィンドウのサイズ変更にも対応しています。

このコードを`particles.html`として保存し、ブラウザで開くと、カラフルなパーティクル効果が表示されます。
User: ファイルに保存してよ。
Assistant: もちろんです。以下に`particles.html`として保存します。

```save particles.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Colorful Particle Effect</title>
    <style>
        body { margin: 0; overflow: hidden; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        // Scene setup
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // Particle system
        const particles = new THREE.BufferGeometry();
        const particleCount = 1000;
        const positions = new Float32Array(particleCount * 3);
        const colors = new Float32Array(particleCount * 3);

        for (let i = 0; i < particleCount; i++) {
            positions[i * 3] = (Math.random() - 0.5) * 10;
            positions[i * 3 + 1] = (Math.random() - 0.5) * 10;
            positions[i * 3 + 2] = (Math.random() - 0.5) * 10;

            colors[i * 3] = Math.random();
            colors[i * 3 + 1] = Math.random();
            colors[i * 3 + 2] = Math.random();
        }

        particles.setAttribute('position', new THREE.BufferAttribute(positions, 3));
        particles.setAttribute('color', new THREE.BufferAttribute(colors, 3));

        const particleMaterial = new THREE.PointsMaterial({
            size: 0.1,
            vertexColors: true,
            transparent: true,
            opacity: 0.8
        });

        const particleSystem = new THREE.Points(particles, particleMaterial);
        scene.add(particleSystem);

        camera.position.z = 5;

        // Animation loop
        function animate() {
            requestAnimationFrame(animate);

            particleSystem.rotation.x += 0.001;
            particleSystem.rotation.y += 0.001;

            renderer.render(scene, camera);
        }

        animate();

        // Handle window resize
        window.addEventListener('resize', () => {
            const width = window.innerWidth;
            const height = window.innerHeight;
            renderer.setSize(width, height);
            camera.aspect = width / height;
            camera.updateProjectionMatrix();
        });
    </script>
</body>
</html>
```
 Save to particles.html? [Y/n]
 Save to particles.html? [Y/n]  Y
Saving to particles.html
System: Saved to particles.html
Assistant: `particles.html`としてファイルに保存しました。ブラウザでこのファイルを開くと、カラフルなパーティクル効果が表示されます。何か他にお手伝いできることがあれば教えてください。

作成されたファイルをローカルに持ってきてブラウザで開くとこんな感じ(実際には動く)

コマンドラインから実行しても対話モードに移っていたけど、これを無効化するには--non-interactive/-nをつける。

$ gptme -n "three.jsを使用して、印象的でカラフルなパーティクル効果を表示するWebアプリをparticles.htmlとして作成して"
WARNING:gptme.cli:Skipping all confirmation prompts.
Found OpenAI API key, using OpenAI provider
[15:28:54] No model specified, using recommended model for provider: gpt-4o
           Using logdir ~/.local/share/gptme/logs/2024-10-07-dancing-silly-rat
           Using workspace at /
Skipped 1 hidden system messages, show with --show-hidden
--- ^^^ past messages ^^^ ---
User: three.jsを使用して、印象的でカラフルなパーティクル効果を表示するWebアプリをparticles.htmlとして作成して
Assistant: Three.jsを使用してカラフルなパーティクル効果を表示するWebアプリを作成するために、以下の手順で`particles.html`を作成します。

1. **Three.jsのインポート**: Three.jsをCDNからインポートします。
2. **シーンのセットアップ**: シーン、カメラ、レンダラーを設定します。
3. **パーティクルの作成**: パーティクルシステムを作成し、カラフルなマテリアルを適用します。
4. **アニメーションの実装**: パーティクルが動くようにアニメーションを設定します。

以下に`particles.html`のコードを示します。

```save particles.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Colorful Particle Effect</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        // シーン、カメラ、レンダラーのセットアップ
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // パーティクルのジオメトリとマテリアルの作成
        const particles = new THREE.BufferGeometry();
        const particleCount = 1000;
        const positions = new Float32Array(particleCount * 3);
        const colors = new Float32Array(particleCount * 3);

        for (let i = 0; i < particleCount; i++) {
            positions[i * 3] = (Math.random() - 0.5) * 10;
            positions[i * 3 + 1] = (Math.random() - 0.5) * 10;
            positions[i * 3 + 2] = (Math.random() - 0.5) * 10;

            colors[i * 3] = Math.random();
            colors[i * 3 + 1] = Math.random();
            colors[i * 3 + 2] = Math.random();
        }

        particles.setAttribute('position', new THREE.BufferAttribute(positions, 3));
        particles.setAttribute('color', new THREE.BufferAttribute(colors, 3));

        const particleMaterial = new THREE.PointsMaterial({
            size: 0.1,
            vertexColors: true,
            transparent: true,
            opacity: 0.8
        });

        const particleSystem = new THREE.Points(particles, particleMaterial);
        scene.add(particleSystem);

        camera.position.z = 5;

        // アニメーションループ
        function animate() {
            requestAnimationFrame(animate);

            particleSystem.rotation.y += 0.001;

            renderer.render(scene, camera);
        }

        animate();

        // ウィンドウサイズ変更時の処理
        window.addEventListener('resize', () => {
            const width = window.innerWidth;
            const height = window.innerHeight;
            renderer.setSize(width, height);
            camera.aspect = width / height;
            camera.updateProjectionMatrix();
        });
    </script>
</body>
</html>
```
Skipping confirmation.
Skipping overwrite confirmation.
Saving to particles.html
System: Saved to particles.html
Assistant: `particles.html`が正常に作成されました。このファイルをブラウザで開くと、Three.jsを使用したカラフルなパーティクル効果が表示されます。パーティクルはランダムな色で生成され、シーン内で回転するように設定されています。ウィンドウのサイズを変更すると、レンダラーとカメラのアスペクト比が自動的に調整されます。

で、シェルに戻ってくる。オプション見てると--no-confirm/-yっていう確認をすべてスキップするオプションが別にあるけど、--non-interactive/-nをつけるとこちらも有効になるみたい。まあそうか。

ハイフンでプロンプトをつなげると、プロンプトチェーン、というか、マクロっぽい感じになる。

$ gptme -n "PythonのREPLでなにかクールなサンプルを実行してみて" - "もっとクールな感じで" - "更にもっとクールな感じで"
WARNING:gptme.cli:Skipping all confirmation prompts.
Found OpenAI API key, using OpenAI provider
[15:35:08] No model specified, using recommended model for provider: gpt-4o
           Using logdir ~/.local/share/gptme/logs/2024-10-07-walking-silly-seal
           Using workspace at /
Skipped 1 hidden system messages, show with --show-hidden
--- ^^^ past messages ^^^ ---
User: PythonのREPLでなにかクールなサンプルを実行してみて
Assistant: PythonのREPLで実行できるクールなサンプルとして、フラクタルの一種であるマンデルブロ集合を描画してみましょう。これは数学的に興味深い図形で、複素数平面上で特定の条件を満たす点の集合です。

以下のコードは、マンデルブロ集合を描画するためのものです。`matplotlib`ライブラリを使用して視覚化します。

```python
import numpy as np
import matplotlib.pyplot as plt

def mandelbrot(c, max_iter):
    z = 0
    n = 0
    while abs(z) <= 2 and n < max_iter:
        z = z*z + c
        n += 1
    return n

def mandelbrot_set(xmin, xmax, ymin, ymax, width, height, max_iter):
    r1 = np.linspace(xmin, xmax, width)
    r2 = np.linspace(ymin, ymax, height)
    return (r1, r2, np.array([[mandelbrot(complex(r, i), max_iter) for r in r1] for i in r2]))

def display(xmin, xmax, ymin, ymax, width, height, max_iter):
    _, _, Z = mandelbrot_set(xmin, xmax, ymin, ymax, width, height, max_iter)
    plt.imshow(Z, extent=(xmin, xmax, ymin, ymax))
    plt.colorbar()
    plt.show()

# Display the Mandelbrot set
display(-2.0, 1.0, -1.5, 1.5, 1000, 1000, 100)
```

このコードを実行すると、マンデルブロ集合の美しい画像が表示されます。それでは、実行してみましょう。                        User: もっとクールな感じで
Assistant: それでは、PythonのREPLで実行できるもう少しクールなサンプルとして、アニメーションでマンデルブロ集合をズームインしていく様子を描画してみましょう。これにより、フラクタルの複雑さと美しさをよりダイナミックに体験できます。

以下のコードは、`matplotlib`のアニメーション機能を使用して、マンデルブロ集合にズームインするアニメーションを作成します。

```python
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

def mandelbrot(c, max_iter):
    z = 0
    n = 0
    while abs(z) <= 2 and n < max_iter:
        z = z*z + c
        n += 1
    return n

def mandelbrot_set(xmin, xmax, ymin, ymax, width, height, max_iter):
    r1 = np.linspace(xmin, xmax, width)
    r2 = np.linspace(ymin, ymax, height)
    return (r1, r2, np.array([[mandelbrot(complex(r, i), max_iter) for r in r1] for i in r2]))

fig, ax = plt.subplots()

def update(frame):
    ax.clear()
    zoom = 1.5 ** (-frame)
    xmin, xmax = -2.0 / zoom, 1.0 / zoom
    ymin, ymax = -1.5 / zoom, 1.5 / zoom
    _, _, Z = mandelbrot_set(xmin, xmax, ymin, ymax, 800, 800, 100)
    ax.imshow(Z, extent=(xmin, xmax, ymin, ymax), cmap='hot')
    ax.set_title(f"Zoom level: {zoom:.2f}")

ani = animation.FuncAnimation(fig, update, frames=30, repeat=False)
plt.show()
```

このコードを実行すると、マンデルブロ集合にズームインしていくアニメーションが表示されます。フラクタルの細部が次第に明らかになっていく様子を楽しんでください。それでは、実行してみましょう。                                                          User: 更にもっとクールな感じで
Assistant: それでは、さらにクールなサンプルとして、PythonのREPLでリアルタイムに色が変化するマンデルブロ集合のアニメーションを作成してみましょう。これにより、フラクタルの美しさを色の変化を通じて楽しむことができます。

以下のコードは、`matplotlib`のアニメーション機能を使用して、色が変化するマンデルブロ集合を描画します。

```python
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

def mandelbrot(c, max_iter):
    z = 0
    n = 0
    while abs(z) <= 2 and n < max_iter:
        z = z*z + c
        n += 1
    return n

def mandelbrot_set(xmin, xmax, ymin, ymax, width, height, max_iter):
    r1 = np.linspace(xmin, xmax, width)
    r2 = np.linspace(ymin, ymax, height)
    return (r1, r2, np.array([[mandelbrot(complex(r, i), max_iter) for r in r1] for i in r2]))

fig, ax = plt.subplots()

def update(frame):
    ax.clear()
    xmin, xmax, ymin, ymax = -2.0, 1.0, -1.5, 1.5
    _, _, Z = mandelbrot_set(xmin, xmax, ymin, ymax, 800, 800, 100)
    ax.imshow(Z, extent=(xmin, xmax, ymin, ymax), cmap='hsv', vmin=0, vmax=frame)
    ax.set_title(f"Color Cycle: {frame}")

ani = animation.FuncAnimation(fig, update, frames=np.linspace(50, 200, 100), repeat=True)
plt.show()
```

このコードを実行すると、マンデルブロ集合が色を変えながら表示されるアニメーションが見られます。色の変化がフラクタルの複雑さを引き立て、視覚的に非常に魅力的です。それでは、実行してみましょう。
kun432kun432

コマンドラインで使えるので、パイプで他のコマンドの出力を受けたり、直接ファイルシステムにアクセスできたりする。

以下のレポジトリを例として使ってみる。

https://github.com/streamlit/chatbot-template

$ git clone https://github.com/streamlit/chatbot-template && cd chatbot-template

ファイルを読み込ませてみる。

$ gptme -n "このコードの内容を要約して" streamlit_app.py
Found OpenAI API key, using OpenAI provider
[15:41:26] No model specified, using recommended model for provider: gpt-4o
           Using logdir ~/.local/share/gptme/logs/2024-10-07-walking-sad-fish
           Using workspace at /chatbot-template
Skipped 1 hidden system messages, show with --show-hidden
--- ^^^ past messages ^^^ ---
User:
このコードの内容を要約して

streamlit_app.py

```streamlit_app.py
import streamlit as st
from openai import OpenAI

# Show title and description.
st.title("💬 Chatbot")
st.write(
    "This is a simple chatbot that uses OpenAI's GPT-3.5 model to generate responses. "
    "To use this app, you need to provide an OpenAI API key, which you can get (https://platform.openai.com/account/
    "You can also learn how to build this app step by step by (https://docs.streamlit.io/develop/t
)

# Ask user for their OpenAI API key via `st.text_input`.
# Alternatively, you can store the API key in `./.streamlit/secrets.toml` and access it
# via `st.secrets`, see https://docs.streamlit.io/develop/concepts/connections/secrets-management
openai_api_key = st.text_input("OpenAI API Key", type="password")
if not openai_api_key:
    st.info("Please add your OpenAI API key to continue.", icon="🗝️")
else:

    # Create an OpenAI client.
    client = OpenAI(api_key=openai_api_key)

    # Create a session state variable to store the chat messages. This ensures that the
    # messages persist across reruns.
    if "messages" not in st.session_state:
        st.session_state.messages = []

    # Display the existing chat messages via `st.chat_message`.
    for message in st.session_state.messages:
        with st.chat_message(message["role"]):
            st.markdown(message["content"])

    # Create a chat input field to allow the user to enter a message. This will display
    # automatically at the bottom of the page.
    if prompt := st.chat_input("What is up?"):

        # Store and display the current prompt.
        st.session_state.messages.append({"role": "user", "content": prompt})
        with st.chat_message("user"):
            st.markdown(prompt)

        # Generate a response using the OpenAI API.
        stream = client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=[
                {"role": m["role"], "content": m["content"]}
                for m in st.session_state.messages
            ],
            stream=True,
        )

        # Stream the response to the chat using `st.write_stream`, then store it in
        # session state.
        with st.chat_message("assistant"):
            response = st.write_stream(stream)
        st.session_state.messages.append({"role": "assistant", "content": response})
```
Assistant: このコードは、Streamlitを使用してOpenAIのGPT-3.5モデルを利用したシンプルなチャットボットアプリを構築するためのものです。以下に要約を示します。

1. **タイトルと説明の表示**: アプリのタイトル「💬 Chatbot」と、OpenAIのGPT-3.5モデルを使用して応答を生成するチャットボットであることを説明します。また、OpenAI APIキーの取得方法とアプリの構築方法についてのリンクも提供しています。

2. **APIキーの入力**: ユーザーにOpenAI APIキーを入力させるための入力フィールドを提供します。APIキーが入力されていない場合は、キーを追加するように促すメッセージを表示します。

3. **OpenAIクライアントの作成**: 入力されたAPIキーを使用してOpenAIクライアントを作成します。

4. **チャットメッセージの管理**: セッション状態にチャットメッセージを保存するための変数を作成し、過去のメッセージを表示します。

5. **チャット入力フィールド**: ユーザーがメッセージを入力できるフィールドを提供し、入力されたメッセージをセッション状態に保存して表示します。

6. **応答の生成**: OpenAI APIを使用してユーザーのメッセージに対する応答を生成し、ストリーミングで表示します。生成された応答もセッション状態に保存します。

このアプリは、ユーザーが入力したメッセージに対してGPT-3.5モデルを用いてリアルタイムで応答を生成し、チャット形式で表示する機能を提供します。

少し編集させてみる

$ gptme -n "タイトルや説明などを日本語化して" streamlit_app.py
WARNING:gptme.cli:Skipping all confirmation prompts.
Found OpenAI API key, using OpenAI provider
[15:43:29] No model specified, using recommended model for provider: gpt-4o
           Using logdir ~/.local/share/gptme/logs/2024-10-07-flying-purple-fish
           Using workspace at /chatbot-template
Skipped 1 hidden system messages, show with --show-hidden
--- ^^^ past messages ^^^ ---
User:
タイトルや説明などを日本語化して

streamlit_app.py

```streamlit_app.py
import streamlit as st
from openai import OpenAI

# Show title and description.
st.title("💬 Chatbot")
st.write(
    "This is a simple chatbot that uses OpenAI's GPT-3.5 model to generate responses. "
    "To use this app, you need to provide an OpenAI API key, which you can get (https://platform.openai.com/account/
    "You can also learn how to build this app step by step by (https://docs.streamlit.io/develop/t
)

# Ask user for their OpenAI API key via `st.text_input`.
# Alternatively, you can store the API key in `./.streamlit/secrets.toml` and access it
# via `st.secrets`, see https://docs.streamlit.io/develop/concepts/connections/secrets-management
openai_api_key = st.text_input("OpenAI API Key", type="password")
if not openai_api_key:
    st.info("Please add your OpenAI API key to continue.", icon="🗝️")
else:

    # Create an OpenAI client.
    client = OpenAI(api_key=openai_api_key)

    # Create a session state variable to store the chat messages. This ensures that the
    # messages persist across reruns.
    if "messages" not in st.session_state:
        st.session_state.messages = []

    # Display the existing chat messages via `st.chat_message`.
    for message in st.session_state.messages:
        with st.chat_message(message["role"]):
            st.markdown(message["content"])

    # Create a chat input field to allow the user to enter a message. This will display
    # automatically at the bottom of the page.
    if prompt := st.chat_input("What is up?"):

        # Store and display the current prompt.
        st.session_state.messages.append({"role": "user", "content": prompt})
        with st.chat_message("user"):
            st.markdown(prompt)

        # Generate a response using the OpenAI API.
        stream = client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=[
                {"role": m["role"], "content": m["content"]}
                for m in st.session_state.messages
            ],
            stream=True,
        )

        # Stream the response to the chat using `st.write_stream`, then store it in
        # session state.
        with st.chat_message("assistant"):
            response = st.write_stream(stream)
        st.session_state.messages.append({"role": "assistant", "content": response})
```
Assistant: Certainly! Let's translate the title and description in the `streamlit_app.py` file to Japanese.

```patch streamlit_app.py
<<<<<<< ORIGINAL
# Show title and description.
st.title("💬 Chatbot")
st.write(
    "This is a simple chatbot that uses OpenAI's GPT-3.5 model to generate responses. "
    "To use this app, you need to provide an OpenAI API key, which you can get [here](https://platform.openai.com/account/api-keys). "
    "You can also learn how to build this app step by step by [following our tutorial](https://docs.streamlit.io/develop/tutorials/llms/build-conversational-apps)."
)
=======
# タイトルと説明を表示します。
st.title("💬 チャットボット")
st.write(
    "これは、OpenAIのGPT-3.5モデルを使用して応答を生成するシンプルなチャットボットです。"
    "このアプリを使用するには、OpenAI APIキーを提供する必要があります。APIキーは[こちら](https://platform.openai.com/account/api-keys)から取得できます。"
    "また、このアプリをステップバイステップで構築する方法を[こちらのチュートリアル](https://docs.streamlit.io/develop/tutorials/llms/build-conversational-apps)で学ぶことができます。"
)
>>>>>>> UPDATED
```
Preview
-# Show title and description.
-st.title("💬 Chatbot")
+# タイトルと説明を表示します。
+st.title("💬 チャットボット")
 st.write(
-    "This is a simple chatbot that uses OpenAI's GPT-3.5 model to generate responses. "
-    "To use this app, you need to provide an OpenAI API key, which you can get [here](https://platform.openai.com/account
-    "You can also learn how to build this app step by step by [following our tutorial](https://docs.streamlit.io/develop/
+    "これは、OpenAIのGPT-3.5モデルを使用して応答を生成するシンプルなチャットボットです。"
+    "このアプリを使用するには、OpenAI APIキーを提供する必要があります。APIキーは[こちら](https://platform.openai.com/acco
+    "また、このアプリをステップバイステップで構築する方法を[こちらのチュートリアル](https://docs.streamlit.io/develop/tut
 )

System: Patch successfully applied to streamlit_app.py
Assistant: The title and description in the `streamlit_app.py` file have been successfully translated to Japanese. If you need further assistance or additional translations, feel free to let me know!

出力が少し英語になってしまったけども、ファイルが編集されている。

$ git diff
diff --git a/streamlit_app.py b/streamlit_app.py
index 1504769..4f016c9 100644
--- a/streamlit_app.py
+++ b/streamlit_app.py
@@ -1,12 +1,12 @@
 import streamlit as st
 from openai import OpenAI

-# Show title and description.
-st.title("💬 Chatbot")
+# タイトルと説明を表示します。
+st.title("💬 チャットボット")
 st.write(
-    "This is a simple chatbot that uses OpenAI's GPT-3.5 model to generate responses. "
-    "To use this app, you need to provide an OpenAI API key, which you can get [here](https://platform.openai.com/account/api-keys). "
-    "You can also learn how to build this app step by step by [following our tutorial](https://docs.streamlit.io/develop/tutorials/llms/build-conversational-apps)."
+    "これは、OpenAIのGPT-3.5モデルを使用して応答を生成するシンプルなチャットボットです。"
+    "このアプリを使用するには、OpenAI APIキーを提供する必要があります。APIキーは[こちら](https://platform.openai.com/account/api-keys)から取得できます。"
+    "また、このアプリをステップバイステップで構築する方法を[こちらのチュートリアル](https://docs.streamlit.io/develop/tutorials/llms/build-conversational-apps)で学ぶことができます。"
 )

 # Ask user for their OpenAI API key via `st.text_input`.

コマンドの実行結果をパイプでgptmeに渡してみる。例として、変更内容を元にコミットメッセージを生成。

$ git diff | gptme -n "この内容からコミットメッセージを作成して"
WARNING:gptme.cli:Skipping all confirmation prompts.
Found OpenAI API key, using OpenAI provider
[15:46:50] No model specified, using recommended model for provider: gpt-4o
           Using logdir ~/.local/share/gptme/logs/2024-10-07-dancing-green-rat
           Using workspace at /chatbot-template
System:
```stdin
diff --git a/streamlit_app.py b/streamlit_app.py
index 1504769..4f016c9 100644
--- a/streamlit_app.py
+++ b/streamlit_app.py
@@ -1,12 +1,12 @@
 import streamlit as st
 from openai import OpenAI

-# Show title and description.
-st.title("💬 Chatbot")
+# タイトルと説明を表示します。
+st.title("💬 チャットボット")
 st.write(
-    "This is a simple chatbot that uses OpenAI's GPT-3.5 model to generate responses. "
-    "To use this app, you need to provide an OpenAI API key, which you can get (https://platform.openai.com/account
-    "You can also learn how to build this app step by step by (https://docs.streamlit.io/develop/
+    "これは、OpenAIのGPT-3.5モデルを使用して応答を生成するシンプルなチャットボットです。"
+    "このアプリを使用するには、OpenAI APIキーを提供する必要があります。APIキーは[こちら](https://platform.openai.com/acco
+    "また、このアプリをステップバイステップで構築する方法を[こちらのチュートリアル](https://docs.streamlit.io/develop/tut
 )

 # Ask user for their OpenAI API key via `st.text_input`.
```
Skipped 1 hidden system messages, show with --show-hidden
--- ^^^ past messages ^^^ ---
User: この内容からコミットメッセージを作成して
Assistant: 以下のようなコミットメッセージが適切です:

```
日本語化: Streamlitアプリのタイトルと説明を日本語に翻訳

- タイトルを「💬 チャットボット」に変更
- 説明文を日本語に翻訳し、OpenAI APIキーの取得方法とチュートリアルへのリンクを含める
```

更にこの結果をパイプで渡すようにしてみる。

$ git add .
$ git diff | gptme --non-interactive "この内容からコミットメッセージを作成して。説明不 要でコミットメッセージだけを出力して" | git commit -F -
WARNING:gptme.cli:Skipping all confirmation prompts.
[main f646aee] Found OpenAI API key, using OpenAI provider [15:54:26] No model specified, using recommended model for provider: gpt-4o            Using logdir ~/.local/share/gptme/logs/2024-10-07-hopping-sad-dragon            Using workspace at /chatbot-template Skipped 1 hidden system messages, show with --show-hidden --- ^^^ past messages ^^^ --- User: この内容Assistant: 新機能: 日本語でのコミットメッセージ生成機能を追加                   istant: Thinking...
 1 file changed, 5 insertions(+), 5 deletions(-)
$ git log
commit faea89d66b10962912fe1db1f3dfaeb8a38f9053 (HEAD -> main)
Author: kun432 <kun432@users.noreply.github.com>
Date:   Mon Oct 7 15:57:52 2024 +0000

    Found OpenAI API key, using OpenAI provider
    [15:57:53] No model specified, using recommended model for provider: gpt-4o
               Using logdir ~/.local/share/gptme/logs/2024-10-07-walking-sleepy-seal
               Using workspace at /chatbot-template
    Skipped 1 hidden system messages, show with --show-hidden
    --- ^^^ past messages ^^^ ---
    User: この内容からコミットメッセージを作成して。説明不要でコミットメッセージだけを出力して
    Assistant: Thinking...^M                                                                                ^MAssistant:
新機能: 日本語でのコミットメッセージ生成機能を追加

うーん、惜しい。examplesにあるように、gptmeの出力結果を更にパイプで渡すには一度ファイルに出力させておくのが良さそう。生成された部分だけを標準出力に渡せるようになるといいなぁ・・・

https://gptme.org/docs/examples.html#commit-message-generator

kun432kun432

gptmeはいろいろなtoolsが用意されている。これを試してみる。上から引き続き。

シェル

$ gptme -n "ファイルの一覧を取得して"
Assistant: 現在のディレクトリ内のファイル一覧を取得します。以下のコマンドを実行します。

```bash
ls
```
LICENSE
README.md
requirements.txt
streamlit_app.py

System:
Ran command: `ls`

stdout:
```
LICENSE
README.md
requirements.txt
streamlit_app.py
```
Assistant: 現在のディレクトリには以下のファイルがあります:

- `LICENSE`
- `README.md`
- `requirements.txt`
- `streamlit_app.py`

これらのファイルについて何か特定の操作や情報が必要であればお知らせください。

Python

pythonというかiPython。

$ gptme -n "100以下のフィボナッチ数を計算するpythonコードを書いて。実行後の結果だけ教えて"
Assistant: ```python
def fibonacci_below_100():
    fib_sequence = []
    a, b = 0, 1
    while a < 100:
        fib_sequence.append(a)
        a, b = b, a + b
    return fib_sequence

fibonacci_below_100()
```

```ipython
def fibonacci_below_100():
    fib_sequence = []
    a, b = 0, 1
    while a < 100:
        fib_sequence.append(a)
        a, b = b, a + b
    return fib_sequence

fibonacci_below_100()
```
Skipping confirmation
Out[1]: System:
Executed code block.

Result:
```
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
```
Assistant: 100以下のフィボナッチ数は次の通りです: `[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]`。

Tmux

長時間のセッション的なものをtmuxで別仮想端末を開いて立ち上げて、gptmeからアクセスするような例が載っている。自分はtmuxを過去使ってこなかったので、スキップ。

Save/Patch

ここは対話モードで試したのでパス。

ブラウザ

URLを入力して情報を取得するだけでなく、ブラウザ操作的なことも行えるっぽい?

$ gptme "https://github.com/ErikBjare/gptme にアクセスして、gptmeの最新バージョンを教えて"
╔════════════════════════════════════════════════════════════╗
║ Looks like Playwright was just installed or updated.       ║
║ Please run the following command to download new browsers: ║
║                                                            ║
║     playwright install                                     ║
║                                                            ║
║ <3 Playwright Team                                         ║
╚════════════════════════════════════════════════════════════╝

ということでplaywrightのブラウザをインストール。依存パッケージもインストールしないといけないはず。あとpandocも必要だった。

$ playwright install --with-deps
$ apt install -y pandoc

再度実行してみる。

$ gptme "https://github.com/ErikBjare/gptme にアクセスして、gptmeの最新バージョンを教えて"
--- ^^^ past messages ^^^ ---
INFO:gptme.tools._browser_playwright:Starting browser
INFO:gptme.tools._browser_playwright:Loading page: https://github.com/ErikBjare/gptme
(snip)

```https://github.com/ErikBjare/gptme
[Skip to content](#start-of-content) [ [] ]

## Navigation Menu

[Toggle navigation]

[ []]
(snip)
## [Releases [50]](/ErikBjare/gptme/releases)

[](/ErikBjare/gptme/releases/tag/v0.19.2)

 [ Latest ]

Oct 7, 2024

[+ 49 releases](/ErikBjare/gptme/releases)
(snip)
```
Assistant: The latest version of gptme on GitHub is v0.19.2, released on October 7, 2024.

参照したURLについてQAもできる。

User: gptmeの作者は?
Assistant: gptmeの作者はErik Bjäreholtです。
User: gptmeのコミット数は?
Assistant: gptmeのリポジトリには、715のコミットがあります。

スクリーンショットも取れる。ちょっと日本語だとうまく指示できなかったので英語で。

User: take a screen shot of gptme's github repo website.
Assistant: I can take a screenshot of the gptme GitHub repository website for you. Let me do that now.

```ipython
screenshot_url("https://github.com/ErikBjare/gptme")
```
Preview
screenshot_url("https://github.com/ErikBjare/gptme")

 Execute code? [Y/n]
 Execute code? [Y/n] Y
INFO:gptme.tools.browser:Taking screenshot of 'https://github.com/ErikBjare/gptme' and saving to 'None'
INFO:gptme.tools._browser_playwright:Loading page: https://github.com/ErikBjare/gptme
Out[1]: System:
Executed code block.

Result:
```
Screenshot saved to /tmp/tmpo_calixj.png
```
Assistant: I have taken a screenshot of the gptme GitHub repository website. You can view the screenshot by accessing the file located at `/tmp/tmpo_calixj.png`. If you need any further assistance, feel free to ask!
$ ls -lt /tmp/tmpo_calixj.png
-rw-r--r-- 1 root root 147106 Oct  7 16:53 /tmp/tmpo_calixj.png

コンテナ内から持ってきたファイルを見てみるとちゃんとキャプチャできていた。

チャットログ

gptmeは起動するたびに会話履歴がログとして保存されている。

$ gptme
New conversation or load previous?

* New conversation
  2024-10-07-sneaking-blue-cat          7 minutes ago      18 msgs
  2024-10-07-skipping-angry-squid       16 minutes ago      2 msgs
  2024-10-07-dancing-hungry-bird        17 minutes ago      2 msgs
  2024-10-07-skipping-happy-walrus      18 minutes ago      2 msgs
  2024-10-07-walking-angry-alien        19 minutes ago      3 msgs
  2024-10-07-hopping-red-alien          21 minutes ago      4 msgs
  2024-10-07-skipping-red-seal          25 minutes ago      2 msgs
  2024-10-07-skipping-blue-rat          27 minutes ago      0 msgs
  Load more

過去の会話もここから呼び出せるようだが、ログファイル名が適当につけられているので、ちょっと内容がわからない。これを検索などできるみたいなのだが、ちょっとうまくいかなかった。

User: Can you find any mentions of "python" in our past conversations?
Assistant: Certainly! I'll^[[D search our past conversations for mentions of "python". Please hold on for a moment.
kun432kun432

gptmeはWebのGUIもある(extrasでserverを指定する必要がある)。どうやら5000番ポートで立ち上がってくるようなので、dockerの場合はポートフォワードしてやる必要がある。ということでコンテナ作り直し。

$ docker run -it --rm -p 30000:5000 python:3.12-slim-bookworm bash
$ apt update
$ apt install -y git
$ pip install gptme[all]
$ gptme  # APIキーを設定

サーバを起動

$ gptme-server

で、ブラウザからアクセスしてみるも繋がらず。

コードを読んでみるとサーバはflaskで書かれていて、flaskはデフォルトだとlocalhostからしかアクセスを受け付けない。

Macなどの自端末上に直接インストールしていれば何も問題ないんだけど、今回はDockerを使っているのでちょっとコードをいじることにする。

$ pip show gptme
Name: gptme
Version: 0.19.2
Summary: A fancy CLI to interact with LLMs in a Chat-style interface, with additional capabilities like executing commands on the local machine.
Home-page:
Author: Erik Bjäreholt
Author-email: erik@bjareho.lt
License: MIT
Location: /usr/local/lib/python3.12/site-packages
Requires: anthropic, bashlex, click, ipython, lxml, multiprocessing-logging, openai, pick, platformdirs, python-dotenv, rich, tabulate, tiktoken, tomlkit, typing-extensions, youtube_transcript_api
Required-by:

/usr/local/lib/python3.12/site-packages以下にgptmeのファイルがあるので、この中のserver/cli.pyをいじる。自分の環境だと/usr/local/lib/python3.12/site-packages/gptme/server/cli.pyだった。

以下の箇所を修正。

    app.run(debug=debug, host='0.0.0.0')

再度サーバ起動

$ gptme-server

ホスト側でブラウザからアクセス。

"New Conversation"をクリック

チャット名を入力

こんな感じでチャットできる。

kun432kun432

gptmeは複数のプロバイダに対応している。

モデルを変更する場合は--model プロバイダ/モデル名で指定する。なお、デフォルトはgpt-4oの様子。これをgpt-4o-miniに変えてみる。

$ gptme --model openai/gpt-4o-mini -n "日本の総理大臣を教えて"
--- ^^^ past messages ^^^ ---
User: 日本の総理大臣を教えて
Assistant: 現在の日本の総理大臣は岸田文雄(きしだ ふみお)です。彼は2021年10月4日に就任しました。岸田氏は自由民主党(自民党)の党首でもあります。

Anthropicにしたい場合はこんな感じ。

$ export ANTHROPIC_API_KEY=XXXXXXXX
$ gptme --model anthropic/claude-3-haiku-20240307 -n "日本の総理大臣を教えて"
User: 日本の総理大臣を教えて
Assistant: はい、分かりました。日本の総理大臣の最近の歴史は以下の通りです:

- 2022年10月に岸田文雄が第100代総理大臣に就任しました。
- 2020年9月に菅義偉が第99代総理大臣に就任し、2021年10月に退任しました。
- 2019年9月に安倍晋三が第98代総理大臣に就任し、2020年8月に退任しました。
- 2017年9月に安倍晋三が第97代総理大臣に就任し、2019年8月に再任されました。
- 2012年12月に安倍晋三が第96代総理大臣に就任し、2014年12月に退任しました。
- 2011年8月に野田佳彦が第95代総理大臣に就任し、2012年12月に退任しました。

このように、近年の日本の総理大臣は安倍晋三氏が長期にわたって務めてきましたが、最近では岸田文雄氏が新しい総理大臣として就任しています。

あとはOpenRouterあたりの例があって、その他AzureなどOpenAI互換なら対応しているとあるが、細かいパラメータの設定はどうすればいいのかな?と思ってコード見てみたけど、どうやら環境変数を設定すればいけそう。

https://github.com/ErikBjare/gptme/blob/7231aa965729e1692b1aa43a36bf93d121516d82/gptme/llm_openai.py#L23-L48

その他についてはLiteLLMでプロキシ立てるなりすればいいと思う

kun432kun432

その他気になるところ

より進んだ自動化の例。GitHub Actionsを使って自動でレビューする例や日々の活動記録を取ったりする例が載っている。
https://gptme.org/docs/automation.html

gptme-evalというCLIがあってどうやら評価等に使えるっぽい?ここはあまり使い方が書いてないのでわからないけど、パブリックな評価用データセットを使えるようにする、みたいなことが書いてある。
https://gptme.org/docs/evals.html

kun432kun432

まとめ

Aiderとは全然違った。あっちはgitと密に連携して明確にペアプログラミングという感じだけども、こちらはコマンドラインとの連携なので、より汎用的にいろいろ使えそう。Issueを見てると、Aiderでできることを取り込もうとしているようなFRがあるので、Aiderを意識して作られているところもあるように思える。

で、やれることはいろいろあるけれど、個人的にはpatch的に更新してくれるのが気に入った。gptmeの--non-interactiveと組み合わせると、最小限の範囲で修正・実行・ダメなら再度修正、みたいな繰り返しみたいなのをほぼ自動でできたりするのは結構見ていて面白い。(修正しきれなくて袋小路にハマったりすることもあるw)

あと、コマンド単体でも使いやすいので、自動化の例にあるようなシェルスクリプトへの組み込みとかもやりやすそう。あとは入力をパイプで受け取ってgptmeに渡すだけじゃなくて、gptmeの出力をパイプで渡せるといいんだけどな(とはいえその場合は出力がぶれたりすることもありそうではあるけど)

ただ、今回Dockerで隔離して使ったように、自動で処理までさせるってのはちょっと怖いところでもある。--non-interactiveをつけた場合は特に。そのあたりは意識しつつ、うまく使いこなせるといろいろ可能性はありそう。

公式のexampleやautomationにある例が参考になる。

https://gptme.org/docs/examples.html

https://gptme.org/docs/automation.html

このスクラップは2ヶ月前にクローズされました