import unittest
from Agents.AIAgent import AIAgent
from tools.program_called_command_list import load_ai_agent_name_list
import flow.flow_controller
import streamlit as st
import ast
class PythonTestProgramerAgent:
def __init__(self, max_check=3):
self.max_check = max_check
name = "PythonTestProgramer"
self.agetn_dict = load_ai_agent_name_list("PythonTestProgramerAgent")
self.agent = AIAgent(name,
self.__get_agent_prompt(name),
[],
False)
self.serch_result = []
self.max_check = max_check
def get_name(self):
return PythonTestProgramerAgent.__name__
def clear_memory(self):
self.agent.clear_memory()
def update_temperature(self, temperature):
self.agent.update_temperature(temperature)
def update_tools(self, tools):
self.agent.update_tools(tools)
def get_respons(self, respons):
codes = flow.flow_controller.get_longest_program_code_from_response(
respons)
if "python" in codes.keys():
result, text = self.run_test(codes["python"])
if result:
return text
return text
respons = self.simple_program("これまでの経緯から、プログラムを一つにまとめて出力してください。")
codes = flow.flow_controller.get_longest_program_code_from_response(
respons)
if "python" in codes.keys():
result, text = self.run_test(codes["python"])
if result:
return text
return text
return "pythonコードを見つけられませんでした"
################################################
def __get_agent_prompt(self, name):
return self.agetn_dict[name][1]
def run_test(self, program_code):
"""
プログラムコードを受け取り、全ての関数・クラスに対するユニットテストを生成・実行。
"""
# 単体テストの中身を作成
self.agent.update_system_prompt(
self.__get_agent_prompt("PythonTestProgramer"))
# ステップ 1: コードを解析して関数・クラスを抽出
result, functions, classes = self.extract_functions_and_classes(
program_code) # エラーの時functionにはエラーの内容が入り、classesはNoneになる。
if result:
# 正常終了した場合。
# ステップ 2: ユニットテストを生成
test_code = self.generate_unit_tests(program_code,
functions, classes)
prompt = (
"次のPythonコードに対するユニットテストの中身を作成してください:\n"
f"{test_code}\n"
"テストはunittestフレームワークを使用し、全ての関数を網羅してください。\
\nユニットテストのテストがある場合は削除してください。"
)
else:
prompt = f"""
コードにエラーがありました。
{functions}
このエラーを直したうえで
ユニットテストを作成してください。テストはunittestフレームワークを使用し、全ての関数を網羅してください。\nユニットテストのテストがある場合は削除してください。
"""
respons = self.agent.get_respons(prompt)
# ステップ 3: テストを実行して結果を取得
error, respons = self.respons_programe_check(respons)
if "" != error:
code_dict =\
flow.flow_controller.get_longest_program_code_from_response(
respons)
print("error:3回テストしましたがエラーをなくせませんでした。")
error_respons = f"""
error:3回テストしましたがエラーをなくせませんでした。プログラムコードは
```python
{code_dict["python"]}
```
エラーの内容は
{error}
"""
return False, error_respons
# ステップ 4: 結果を判定
return True, respons
def extract_functions_and_classes(self, code):
functions = []
classes = {}
"""
コードを解析して、関数、クラス、クラス内メソッドを抽出する。
"""
try:
tree = ast.parse(code)
# ... treeを使うコードの残りの部分 ...
except IndentationError as e:
print(f"IndentationError: {e}")
return False, "IndentationError: {e}", None
# エラーを処理します(例:ログに記録する、ユーザーフレンドリーなメッセージを表示する、treeにデフォルト値を使用するなど)
except SyntaxError as e:
print(f"SyntaxError: {e}")
# エラーを処理します(例:ログに記録する、ユーザーフレンドリーなメッセージを表示する、treeにデフォルト値を使用するなど)
return False, "SyntaxError: {e}", None
except Exception as e: # パース中のその他の潜在的なエラーをキャッチします
print(f"予期せぬエラーが発生しました: {e}")
return False, "予期せぬエラーが発生しました: {e}", None
# コードが正常だった場合
for node in ast.walk(tree):
# トップレベルの関数を抽出
if isinstance(node, ast.FunctionDef):
functions.append(node.name)
# クラスとそのメソッドを抽出
if isinstance(node, ast.ClassDef):
methods = [
n.name for n in node.body if isinstance(n, ast.FunctionDef)
]
classes[node.name] = methods
return True, functions, classes
def generate_unit_tests(self, program_code, functions, classes):
"""
関数とクラスに対するユニットテストを生成。
"""
test_cases = []
# 各関数に対するテストコードを生成
for func in functions:
test_cases.append(f"""
class Test{func.capitalize()}(unittest.TestCase):
def test_{func}(self):
# 関数 {func} のテスト
# 入力例と期待される出力を指定
# self.assertEqual({func}(...), ...)
pass
""")
# 各クラスに対するテストコードを生成
for cls, methods in classes.items():
method_tests = "\n".join([
f"""
def test_{method}(self):
# クラス {cls} のメソッド {method} のテスト
# 例: obj = {cls}(...); result = obj.{method}(...); self.assertEqual(result, ...)
pass
"""
for method in methods
])
test_cases.append(f"""
class Test{cls}(unittest.TestCase):
# クラス {cls} に属するメソッドのテストケース
{method_tests}
""")
# プログラムコードとテストコードを組み合わせる
test_code = f"""
{program_code}
import unittest
{"".join(test_cases)}
if __name__ == "__main__":
unittest.main()
"""
return test_code
def run_unit_tests(self, respons):
"""
ユニットテストコードを実行し、結果を返す。
"""
exec_globals = {}
error, respons = self.respons_programe_check(respons)
if "" == error:
try:
code_dict =\
flow.flow_controller.get_longest_program_code_from_response(respons)
if "python" in code_dict.keys():
test_code = code_dict["python"]
exec(test_code, exec_globals)
suite = unittest.defaultTestLoader.loadTestsFromModule(
exec_globals)
runner = unittest.TextTestRunner()
result = runner.run(suite)
return result
except Exception as e:
print(f"テストの実行中にエラーが発生しました: {e}")
return None
return None
################################################
def simple_program(self, command):
self.agent.update_system_prompt(
self.__get_agent_prompt("PythonProgramer"))
return self.agent.get_respons(command)
def program(self, command):
self.agent.update_system_prompt(
self.__get_agent_prompt("PythonProgramer"))
# コールバック関数の設定 (エージェントの動作の可視化用)
respons = self.agent.get_respons(command)
return self.respons_programe_check(respons)
def respons_programe_check(self, check_prompt):
# 次の作業者を決定。
count = 0
respons_text = check_prompt
# pythonで エラーが出るようなら自動で作りなおしを依頼。
while True:
error_respons = flow.flow_controller.python_error_check(
respons_text)
if False is flow.flow_controller.get_code_error():
error_respons = ""
return error_respons, respons_text
if self.max_check <= count:
return error_respons, respons_text
print(error_respons)
respons_text = self.think_agent(
error_respons + "\r\n原因を起こしている記述とそこに関連している部分を比較して原因を特定してください。原因がわかったら、直してください。"
)
count += 1
def think_agent(self, prompt):
# AIエージェントの思考
with st.chat_message("PythonProgramer"):
# エージェントを実行
response = self.agent.get_respons(prompt)
st.write(response)
return response