はじめに
ChatGPTのAPIがリリースされたのでMaya上で動作させてみました。ただ会話するだけではつまらないので、Pythonスクリプトを書いてもらってそのまま実行するUIを作成してみます。
テスト環境
- Maya 2023 (Python 3.9.7)
- openai 0.27.0
Mayaにopenai
を入れる
まずはMayaのPythonからopenai
モジュールを呼び出せないといけないので、以下の方法でインストールしました。
- Anaconda3で仮想環境を作成。Mayaと同じバージョンのPythonを入れる
- 仮想環境に
openai
を入れる -
PYTHONPATH
に仮想環境のパッケージフォルダ~\site-packages
を追加し、Maya起動
conda create -n maya2023 python=3.9.7
conda activate maya2023
pip install openai
@echo off
set PYTHONPATH=%UserProfile%\Anaconda3\envs\maya2023\Lib\site-packages
start "" "%ProgramFiles%\Autodesk\Maya2023\bin\maya.exe"
exit
mayapyとpipでMayaのインストールフォルダに直接パッケージを入れる方法もありますが、Maya本体には手を入れたくないためこのような方法を取っています。
実装
APIコールを行う関数を準備
import os
import openai
openai.api_key = os.getenv("OPENAI_API_KEY")
def completion(new_message_text:str, settings_text:str = '', past_messages:list = []):
if len(past_messages) == 0 and len(settings_text) != 0:
system = {"role": "system", "content": settings_text}
past_messages.append(system)
new_message = {"role": "user", "content": new_message_text}
past_messages.append(new_message)
result = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=past_messages
)
message_text = result.choices[0].message.content
response_message = {"role": "assistant", "content": message_text}
past_messages.append(response_message)
return message_text, past_messages
こちらは以下の記事を大いに参考にさせていただいてます。(ほぼコピペですが...)
返答をPythonコードとその他の部分に分解
re
モジュールを使って```python
~```
で囲まれた部分とそれ以外の部分を切り出します。
import re
def decompose_response(txt):
pattern = r"```python([\s\S]*?)```"
code_list = re.findall(pattern, txt)
for i in range(int(len(code_list))):
code_list[i] = re.sub('\A[\r?\n]', '', code_list[i])
code_list[i] = re.sub('[\r?\n]\Z', '', code_list[i])
comment = re.sub(pattern, '', txt)
comment = re.sub('[\r?\n]+', '\n', comment)
comment = re.sub('[\r?\n]\Z', '', comment)
return comment, code_list
例えば返答がこんな感じだった場合、
ああああああああああああ。
```python
import os
```
いいいいいいいいいいいいいいい。
```python
import maya.cmds as cmds
```
うううううううううううううううううう。
戻り値はこうなります。
comment, code_list = decompose_response(txt)
comment
# Result: ああああああああああああ。
# いいいいいいいいいいいいいいい。
# うううううううううううううううううう。
code_list
# Result: ['import os', 'import maya.cmds as cmds']
UI作成
いよいよMayaでUIを作成します。
かなり即席です。ChatGPTがコードブロックを複数書いてきた場合2つ目以降は消えてなくなります(汗
from maya import cmds
class ChatGPT_Maya(object):
def __init__(self):
self.system_settings = "質問に対して、MayaのPythonスクリプトを書いてください。スクリプト以外の文章は短めにまとめてください。"
self.message_log = []
self.at_first = True
self.create_window()
def reset_session(self, *args):
self.message_log = []
self.at_first = True
cmds.scrollField(self.input_field, e=True, tx='')
cmds.scrollField(self.ai_comment, e=True, tx='')
cmds.cmdScrollFieldExecuter(self.script_field, e=True, t='')
def call(self, *args):
user_input = cmds.scrollField(self.input_field, q=True, tx=True)
# APIコール
if self.at_first:
message_text, self.message_log = completion(user_input, self.system_settings, [])
self.at_first = False
else:
message_text, self.message_log = completion(user_input, '', self.message_log)
# 返答を分解
comment, code_list = decompose_response(message_text)
# Pythonコード以外の部分をai_commentに表示
cmds.scrollField(self.ai_comment, e=True, tx=comment)
# Pythonコードの1つ目をscript_fieldに表示。2つ目以降は無視(汗
if code_list:
cmds.cmdScrollFieldExecuter(self.script_field, e=True, t=code_list[0])
# 実行
if cmds.checkBox(self.script_exec, q=True, v=True):
cmds.cmdScrollFieldExecuter(self.script_field, e=True, executeAll=True)
else:
cmds.cmdScrollFieldExecuter(self.script_field, e=True, t='')
# UI作成
def create_window(self, *args):
cmds.window(title=u'ChatGPTがPythonスクリプトを書くよ!', width=600, sizeable=True)
cmds.columnLayout(adj=True, cat=['both',5], rs=5)
self.reset_button = cmds.button(label='Reset', c=self.reset_session) #セッションのリセット
self.input_field = cmds.scrollField(h=50, ed=True, ww=True, tx='', ec=self.call) #テンキーのEnterで送信
self.script_exec = cmds.checkBox(label=u'実行もする', align='left', v=True) #Checkが入っていたら返答と同時にスクリプトを実行
cmds.separator(h=10, st='in')
self.ai_comment = cmds.scrollField(h=100, ed=False, ww=True, tx='') #コードブロック以外の部分を表示する場所
cmds.text(l='Script:', align='left')
self.script_field = cmds.cmdScrollFieldExecuter(st='python', h=200) #Pythonコードを表示・実行する場所
cmds.setParent('..')
cmds.showWindow()
こんなかんじのUIが出来ました!ウィンドウを閉じるかResetボタンを押すまでは、直前までの会話を記憶した状態になります。
【2023/3/6 追記】少しだけ改良したものをGithubにアップしたのでリンクを記載しました。
【2023/4/18 追記】アップデートが進んでしまったので、執筆時点のコミットへリンクを差し替えました。