add git function

This commit is contained in:
thinkwee 2023-09-25 16:43:01 +08:00
parent de203ee41e
commit 75fbb9a590
8 changed files with 87 additions and 40 deletions

View File

@ -27,9 +27,15 @@
## 📰 News ## 📰 News
* **September 25th, 2023: The **Git** feature is now available**, enabling the programmer < img src='online_log/static/figures/programmer.png' height=20> to utilize GitHub for version control. To enable this feature, simply set ``"git_management"`` to ``"True"`` in ``ChatChainConfig.json``.
<p align="center">
<img src='./misc/github.png' width=600>
</p>
* September 20th, 2023: The **Human-Agent-Interaction** mode is now available! You can get involved with the ChatDev team by playing the role of reviewer <img src='online_log/static/figures/reviewer.png' height=20> and making suggestions to the programmer <img src='online_log/static/figures/programmer.png' height=20>; * September 20th, 2023: The **Human-Agent-Interaction** mode is now available! You can get involved with the ChatDev team by playing the role of reviewer <img src='online_log/static/figures/reviewer.png' height=20> and making suggestions to the programmer <img src='online_log/static/figures/programmer.png' height=20>;
try ``python3 run.py --task [description_of_your_idea] --config "Human"``. See [guide](wiki.md#human-agent-interaction) and [example](WareHouse/Gomoku_HumanAgentInteraction_20230920135038). try ``python3 run.py --task [description_of_your_idea] --config "Human"``. See [guide](wiki.md#human-agent-interaction) and [example](WareHouse/Gomoku_HumanAgentInteraction_20230920135038).
<center><img src='misc/Human_intro.png' width=800></center> <p align="center">
<img src='./misc/Human_intro.png' width=600>
</p>
* September 1st, 2023: The **Art** mode is available now! You can activate the designer agent <img src='online_log/static/figures/designer.png' height=20> to generate images used in the software; * September 1st, 2023: The **Art** mode is available now! You can activate the designer agent <img src='online_log/static/figures/designer.png' height=20> to generate images used in the software;
try ``python3 run.py --task [description_of_your_idea] --config "Art"``. See [guide](wiki.md#art) and [example](WareHouse/gomokugameArtExample_THUNLP_20230831122822). try ``python3 run.py --task [description_of_your_idea] --config "Art"``. See [guide](wiki.md#art) and [example](WareHouse/gomokugameArtExample_THUNLP_20230831122822).
* August 28th, 2023: The system is publicly available. * August 28th, 2023: The system is publicly available.
@ -165,10 +171,9 @@ create a software package and generate a folder named ``/WareHouse/2048_THUNLP_t
## ⚖️ License ## ⚖️ License
- The purpose of ChatDev is exclusively for research purposes. - Source Code Licensing: Our project's source code is licensed under the Apache 2.0 License. This license permits the use, modification, and distribution of the code, subject to certain conditions outlined in the Apache 2.0 License.
- The source code is licensed under Apache 2.0. - Project Open-Source Status: The project is indeed open-source; however, this designation is primarily intended for non-commercial purposes. While we encourage collaboration and contributions from the community for research and non-commercial applications, it is important to note that any utilization of the project's components for commercial purposes necessitates separate licensing agreements.
- The datasets are licensed under CC BY NC 4.0, which allows for non-commercial use only. It is important to note that - Data Licensing: The related data utilized in our project is licensed under CC BY-NC 4.0. This license explicitly permits non-commercial use of the data. We would like to emphasize that any models trained using these datasets should strictly adhere to the non-commercial usage restriction and should be employed exclusively for research purposes.
any models trained using these datasets should not be employed for purposes other than research.
## Star History ## Star History

View File

@ -60,7 +60,6 @@ class PhaseType(Enum):
RECRUITING_CPO = "recruiting CPO" RECRUITING_CPO = "recruiting CPO"
RECRUITING_CTO = "recruiting CTO" RECRUITING_CTO = "recruiting CTO"
DEMAND_ANALYSIS = "demand analysis" DEMAND_ANALYSIS = "demand analysis"
BRAINSTORMING = "brainstorming"
CHOOSING_LANGUAGE = "choosing language" CHOOSING_LANGUAGE = "choosing language"
RECRUITING_PROGRAMMER = "recruiting programmer" RECRUITING_PROGRAMMER = "recruiting programmer"
RECRUITING_REVIEWER = "recruiting reviewer" RECRUITING_REVIEWER = "recruiting reviewer"

View File

@ -1,10 +1,10 @@
import importlib import importlib
import json import json
import logging
import os import os
import shutil import shutil
from datetime import datetime
import logging
import time import time
from datetime import datetime
from camel.agents import RolePlaying from camel.agents import RolePlaying
from camel.configs import ChatGPTConfig from camel.configs import ChatGPTConfig
@ -63,7 +63,6 @@ class ChatChain:
# init ChatEnv # init ChatEnv
self.chat_env_config = ChatEnvConfig(clear_structure=check_bool(self.config["clear_structure"]), self.chat_env_config = ChatEnvConfig(clear_structure=check_bool(self.config["clear_structure"]),
brainstorming=check_bool(self.config["brainstorming"]),
gui_design=check_bool(self.config["gui_design"]), gui_design=check_bool(self.config["gui_design"]),
git_management=check_bool(self.config["git_management"])) git_management=check_bool(self.config["git_management"]))
self.chat_env = ChatEnv(self.chat_env_config) self.chat_env = ChatEnv(self.chat_env_config)
@ -102,8 +101,6 @@ class ChatChain:
log_filepath=self.log_filepath) log_filepath=self.log_filepath)
self.phases[phase] = phase_instance self.phases[phase] = phase_instance
def make_recruitment(self): def make_recruitment(self):
""" """
recruit all employees recruit all employees
@ -176,7 +173,8 @@ class ChatChain:
root = os.path.dirname(filepath) root = os.path.dirname(filepath)
# directory = root + "/WareHouse/" # directory = root + "/WareHouse/"
directory = os.path.join(root, "WareHouse") directory = os.path.join(root, "WareHouse")
log_filepath = os.path.join(directory, "{}.log".format("_".join([self.project_name, self.org_name,start_time]))) log_filepath = os.path.join(directory,
"{}.log".format("_".join([self.project_name, self.org_name, start_time])))
return start_time, log_filepath return start_time, log_filepath
def pre_processing(self): def pre_processing(self):
@ -187,9 +185,7 @@ class ChatChain:
""" """
if self.chat_env.config.clear_structure: if self.chat_env.config.clear_structure:
filepath = os.path.dirname(__file__) filepath = os.path.dirname(__file__)
# root = "/".join(filepath.split("/")[:-1])
root = os.path.dirname(filepath) root = os.path.dirname(filepath)
# directory = root + "/WareHouse"
directory = os.path.join(root, "WareHouse") directory = os.path.join(root, "WareHouse")
for filename in os.listdir(directory): for filename in os.listdir(directory):
file_path = os.path.join(directory, filename) file_path = os.path.join(directory, filename)
@ -221,8 +217,8 @@ class ChatChain:
preprocess_msg += "**task_prompt**: {}\n\n".format(self.task_prompt_raw) preprocess_msg += "**task_prompt**: {}\n\n".format(self.task_prompt_raw)
preprocess_msg += "**project_name**: {}\n\n".format(self.project_name) preprocess_msg += "**project_name**: {}\n\n".format(self.project_name)
preprocess_msg += "**Log File**: {}\n\n".format(self.log_filepath) preprocess_msg += "**Log File**: {}\n\n".format(self.log_filepath)
preprocess_msg += "**ChatDevConfig**:\n {}\n\n".format(self.chat_env.config.__str__()) preprocess_msg += "**ChatDevConfig**:\n{}\n\n".format(self.chat_env.config.__str__())
preprocess_msg += "**ChatGPTConfig**:\n {}\n\n".format(chat_gpt_config) preprocess_msg += "**ChatGPTConfig**:\n{}\n\n".format(chat_gpt_config)
log_and_print_online(preprocess_msg) log_and_print_online(preprocess_msg)
# init task prompt # init task prompt
@ -240,9 +236,33 @@ class ChatChain:
self.chat_env.write_meta() self.chat_env.write_meta()
filepath = os.path.dirname(__file__) filepath = os.path.dirname(__file__)
# root = "/".join(filepath.split("/")[:-1])
root = os.path.dirname(filepath) root = os.path.dirname(filepath)
if self.chat_env_config.git_management:
git_online_log = "**[Git Information]**\n\n"
self.chat_env.codes.version += 1
os.system("cd {}; git add .".format(self.chat_env.env_dict["directory"]))
git_online_log += "cd {}; git add .\n".format(self.chat_env.env_dict["directory"])
os.system("cd {}; git commit -m \"v{} Final Version\"".format(self.chat_env.env_dict["directory"], self.chat_env.codes.version))
git_online_log += "cd {}; git commit -m \"v{} Final Version\"\n".format(self.chat_env.env_dict["directory"], self.chat_env.codes.version)
log_and_print_online(git_online_log)
git_info = "**[Git Log]**\n\n"
import subprocess
# 执行git log命令
command = "cd {}; git log".format(self.chat_env.env_dict["directory"])
completed_process = subprocess.run(command, shell=True, text=True, stdout=subprocess.PIPE)
if completed_process.returncode == 0:
log_output = completed_process.stdout
else:
log_output = "Error when executing " + command
git_info += log_output
log_and_print_online(git_info)
post_info = "**[Post Info]**\n\n" post_info = "**[Post Info]**\n\n"
now_time = now() now_time = now()
time_format = "%Y%m%d%H%M%S" time_format = "%Y%m%d%H%M%S"
@ -251,7 +271,8 @@ class ChatChain:
duration = (datetime2 - datetime1).total_seconds() duration = (datetime2 - datetime1).total_seconds()
post_info += "Software Info: {}".format( post_info += "Software Info: {}".format(
get_info(self.chat_env.env_dict['directory'], self.log_filepath) + "\n\n🕑**duration**={:.2f}s\n\n".format(duration)) get_info(self.chat_env.env_dict['directory'], self.log_filepath) + "\n\n🕑**duration**={:.2f}s\n\n".format(
duration))
post_info += "ChatDev Starts ({})".format(self.start_time) + "\n\n" post_info += "ChatDev Starts ({})".format(self.start_time) + "\n\n"
post_info += "ChatDev Ends ({})".format(now_time) + "\n\n" post_info += "ChatDev Ends ({})".format(now_time) + "\n\n"

View File

@ -17,18 +17,17 @@ from chatdev.utils import log_and_print_online
class ChatEnvConfig: class ChatEnvConfig:
def __init__(self, clear_structure, def __init__(self, clear_structure,
brainstorming,
gui_design, gui_design,
git_management): git_management):
self.clear_structure = clear_structure self.clear_structure = clear_structure
self.brainstorming = brainstorming
self.gui_design = gui_design self.gui_design = gui_design
self.git_management = git_management self.git_management = git_management
def __str__(self): def __str__(self):
string = "" string = ""
string += "ChatEnvConfig.clear_structure: {}\n".format(self.clear_structure) string += "ChatEnvConfig.clear_structure: {}\n".format(self.clear_structure)
string += "ChatEnvConfig.brainstorming: {}\n".format(self.brainstorming) string += "ChatEnvConfig.git_management: {}\n".format(self.git_management)
string += "ChatEnvConfig.gui_design: {}\n".format(self.gui_design)
return string return string
@ -112,7 +111,7 @@ class ChatEnv:
else: else:
os.kill(process.pid, signal.SIGTERM) os.kill(process.pid, signal.SIGTERM)
if process.poll() is None: if process.poll() is None:
os.kill(process.pid,signal.CTRL_BREAK_EVENT) os.kill(process.pid, signal.CTRL_BREAK_EVENT)
if return_code == 0: if return_code == 0:
return False, success_info return False, success_info
@ -143,8 +142,8 @@ class ChatEnv:
def update_codes(self, generated_content): def update_codes(self, generated_content):
self.codes._update_codes(generated_content) self.codes._update_codes(generated_content)
def rewrite_codes(self) -> None: def rewrite_codes(self, phase_info=None) -> None:
self.codes._rewrite_codes(self.config.git_management) self.codes._rewrite_codes(self.config.git_management, phase_info)
def get_codes(self) -> str: def get_codes(self) -> str:
return self.codes._get_codes() return self.codes._get_codes()

View File

@ -1,13 +1,15 @@
import difflib
import os import os
import re import re
import subprocess
from chatdev.utils import log_and_print_online from chatdev.utils import log_and_print_online
import difflib
class Codes: class Codes:
def __init__(self, generated_content=""): def __init__(self, generated_content=""):
self.directory: str = None self.directory: str = None
self.version: float = 1.0 self.version: float = 0.0
self.generated_content: str = generated_content self.generated_content: str = generated_content
self.codebooks = {} self.codebooks = {}
@ -71,7 +73,7 @@ class Codes:
log_and_print_online(update_codes_content) log_and_print_online(update_codes_content)
self.codebooks[key] = new_codes.codebooks[key] self.codebooks[key] = new_codes.codebooks[key]
def _rewrite_codes(self, git_management) -> None: def _rewrite_codes(self, git_management, phase_info=None) -> None:
directory = self.directory directory = self.directory
rewrite_codes_content = "**[Rewrite Codes]**\n\n" rewrite_codes_content = "**[Rewrite Codes]**\n\n"
if os.path.exists(directory) and len(os.listdir(directory)) > 0: if os.path.exists(directory) and len(os.listdir(directory)) > 0:
@ -87,12 +89,35 @@ class Codes:
rewrite_codes_content += os.path.join(directory, filename) + " Wrote\n" rewrite_codes_content += os.path.join(directory, filename) + " Wrote\n"
if git_management: if git_management:
if not phase_info:
phase_info = ""
git_online_log = "**[Git Information]**\n\n"
if self.version == 1.0: if self.version == 1.0:
os.system("cd {}; git init".format(self.directory)) os.system("cd {}; git init".format(self.directory))
git_online_log += "cd {}; git init\n".format(self.directory)
os.system("cd {}; git add .".format(self.directory)) os.system("cd {}; git add .".format(self.directory))
os.system("cd {}; git commit -m \"{}\"".format(self.directory, self.version)) git_online_log += "cd {}; git add .\n".format(self.directory)
log_and_print_online(rewrite_codes_content) # check if there exist diff
completed_process = subprocess.run("cd {}; git status".format(self.directory), shell=True, text=True,
stdout=subprocess.PIPE)
if "nothing to commit" in completed_process.stdout:
self.version -= 1.0
return
os.system("cd {}; git commit -m \"v{}\"".format(self.directory, str(self.version) + " " + phase_info))
git_online_log += "cd {}; git commit -m \"v{}\"\n".format(self.directory,
str(self.version) + " " + phase_info)
if self.version == 1.0:
os.system("cd {}; git submodule add ./{} {}".format(os.path.dirname(os.path.dirname(self.directory)),
"WareHouse/" + os.path.basename(self.directory),
"WareHouse/" + os.path.basename(self.directory)))
git_online_log += "cd {}; git submodule add ./{} {}\n".format(
os.path.dirname(os.path.dirname(self.directory)),
"WareHouse/" + os.path.basename(self.directory),
"WareHouse/" + os.path.basename(self.directory))
log_and_print_online(rewrite_codes_content)
log_and_print_online(git_online_log)
def _get_codes(self) -> str: def _get_codes(self) -> str:
content = "" content = ""

View File

@ -135,7 +135,7 @@ class ComposedPhase(ABC):
""" """
self.update_phase_env(chat_env) self.update_phase_env(chat_env)
for cycle_index in range(self.cycle_num): for cycle_index in range(1, self.cycle_num + 1):
for phase_item in self.composition: for phase_item in self.composition:
assert phase_item["phaseType"] == "SimplePhase" # right now we do not support nested composition assert phase_item["phaseType"] == "SimplePhase" # right now we do not support nested composition
phase = phase_item['phase'] phase = phase_item['phase']

View File

@ -207,8 +207,6 @@ class Phase(ABC):
question = """Answer their final discussed conclusion (Yes or No) in the discussion without any other words, e.g., "Yes" """ question = """Answer their final discussed conclusion (Yes or No) in the discussion without any other words, e.g., "Yes" """
elif phase_name == "DemandAnalysis": elif phase_name == "DemandAnalysis":
question = """Answer their final product modality in the discussion without any other words, e.g., "PowerPoint" """ question = """Answer their final product modality in the discussion without any other words, e.g., "PowerPoint" """
# elif phase_name in [PhaseType.BRAINSTORMING]:
# question = """Conclude three most creative and imaginative brainstorm ideas from the whole discussion, in the format: "1) *; 2) *; 3) *; where '*' represents a suggestion." """
elif phase_name == "LanguageChoose": elif phase_name == "LanguageChoose":
question = """Conclude the programming language being discussed for software development, in the format: "*" where '*' represents a programming language." """ question = """Conclude the programming language being discussed for software development, in the format: "*" where '*' represents a programming language." """
elif phase_name == "EnvironmentDoc": elif phase_name == "EnvironmentDoc":
@ -356,7 +354,7 @@ class Coding(Phase):
chat_env.update_codes(self.seminar_conclusion) chat_env.update_codes(self.seminar_conclusion)
if len(chat_env.codes.codebooks.keys()) == 0: if len(chat_env.codes.codebooks.keys()) == 0:
raise ValueError("No Valid Codes.") raise ValueError("No Valid Codes.")
chat_env.rewrite_codes() chat_env.rewrite_codes("Finish Coding")
log_and_print_online( log_and_print_online(
"**[Software Info]**:\n\n {}".format(get_info(chat_env.env_dict['directory'], self.log_filepath))) "**[Software Info]**:\n\n {}".format(get_info(chat_env.env_dict['directory'], self.log_filepath)))
return chat_env return chat_env
@ -392,7 +390,7 @@ class ArtIntegration(Phase):
def update_chat_env(self, chat_env) -> ChatEnv: def update_chat_env(self, chat_env) -> ChatEnv:
chat_env.update_codes(self.seminar_conclusion) chat_env.update_codes(self.seminar_conclusion)
chat_env.rewrite_codes() chat_env.rewrite_codes("Finish Art Integration")
# chat_env.generate_images_from_codes() # chat_env.generate_images_from_codes()
log_and_print_online( log_and_print_online(
"**[Software Info]**:\n\n {}".format(get_info(chat_env.env_dict['directory'], self.log_filepath))) "**[Software Info]**:\n\n {}".format(get_info(chat_env.env_dict['directory'], self.log_filepath)))
@ -424,7 +422,7 @@ class CodeComplete(Phase):
chat_env.update_codes(self.seminar_conclusion) chat_env.update_codes(self.seminar_conclusion)
if len(chat_env.codes.codebooks.keys()) == 0: if len(chat_env.codes.codebooks.keys()) == 0:
raise ValueError("No Valid Codes.") raise ValueError("No Valid Codes.")
chat_env.rewrite_codes() chat_env.rewrite_codes("Code Complete #" + str(self.phase_env["cycle_index"]) + " Finished")
log_and_print_online( log_and_print_online(
"**[Software Info]**:\n\n {}".format(get_info(chat_env.env_dict['directory'], self.log_filepath))) "**[Software Info]**:\n\n {}".format(get_info(chat_env.env_dict['directory'], self.log_filepath)))
return chat_env return chat_env
@ -463,7 +461,7 @@ class CodeReviewModification(Phase):
def update_chat_env(self, chat_env) -> ChatEnv: def update_chat_env(self, chat_env) -> ChatEnv:
if "```".lower() in self.seminar_conclusion.lower(): if "```".lower() in self.seminar_conclusion.lower():
chat_env.update_codes(self.seminar_conclusion) chat_env.update_codes(self.seminar_conclusion)
chat_env.rewrite_codes() chat_env.rewrite_codes("Review #" + str(self.phase_env["cycle_index"]) + " Finished")
log_and_print_online( log_and_print_online(
"**[Software Info]**:\n\n {}".format(get_info(chat_env.env_dict['directory'], self.log_filepath))) "**[Software Info]**:\n\n {}".format(get_info(chat_env.env_dict['directory'], self.log_filepath)))
self.phase_env['modification_conclusion'] = self.seminar_conclusion self.phase_env['modification_conclusion'] = self.seminar_conclusion
@ -484,7 +482,7 @@ class CodeReviewHuman(Phase):
def update_chat_env(self, chat_env) -> ChatEnv: def update_chat_env(self, chat_env) -> ChatEnv:
if "```".lower() in self.seminar_conclusion.lower(): if "```".lower() in self.seminar_conclusion.lower():
chat_env.update_codes(self.seminar_conclusion) chat_env.update_codes(self.seminar_conclusion)
chat_env.rewrite_codes() chat_env.rewrite_codes("Human Review #" + str(self.phase_env["cycle_index"]) + " Finished")
log_and_print_online( log_and_print_online(
"**[Software Info]**:\n\n {}".format(get_info(chat_env.env_dict['directory'], self.log_filepath))) "**[Software Info]**:\n\n {}".format(get_info(chat_env.env_dict['directory'], self.log_filepath)))
return chat_env return chat_env
@ -496,14 +494,14 @@ class CodeReviewHuman(Phase):
f"Now you can participate in the development of the software!\n" f"Now you can participate in the development of the software!\n"
f"The task is: {chat_env.env_dict['task_prompt']}\n" f"The task is: {chat_env.env_dict['task_prompt']}\n"
f"Please input your feedback (in one line). It can be bug report or new feature requirement.\n" f"Please input your feedback (in one line). It can be bug report or new feature requirement.\n"
f"You are currently in the #{self.phase_env['cycle_index'] + 1} human feedback with a total of {self.phase_env['cycle_num']} feedbacks\n" f"You are currently in the #{self.phase_env['cycle_index']} human feedback with a total of {self.phase_env['cycle_num']} feedbacks\n"
f"Press [Enter] to submit.\n" f"Press [Enter] to submit.\n"
f"You can type \"End\" to quit this mode at any time.\n" f"You can type \"End\" to quit this mode at any time.\n"
) )
provided_comments = input(">>> ") provided_comments = input(">>> ")
self.phase_env["comments"] = provided_comments self.phase_env["comments"] = provided_comments
log_and_print_online( log_and_print_online(
f"**[User Provided Comments]**\n\n In the #{self.phase_env['cycle_index'] + 1} of total {self.phase_env['cycle_num']} comments: \n\n" + provided_comments) f"**[User Provided Comments]**\n\n In the #{self.phase_env['cycle_index']} of total {self.phase_env['cycle_num']} comments: \n\n" + provided_comments)
if provided_comments.lower() == "end": if provided_comments.lower() == "end":
return chat_env return chat_env
@ -592,7 +590,7 @@ class TestModification(Phase):
def update_chat_env(self, chat_env) -> ChatEnv: def update_chat_env(self, chat_env) -> ChatEnv:
if "```".lower() in self.seminar_conclusion.lower(): if "```".lower() in self.seminar_conclusion.lower():
chat_env.update_codes(self.seminar_conclusion) chat_env.update_codes(self.seminar_conclusion)
chat_env.rewrite_codes() chat_env.rewrite_codes("Test #" + str(self.phase_env["cycle_index"]) + " Finished")
log_and_print_online( log_and_print_online(
"**[Software Info]**:\n\n {}".format(get_info(chat_env.env_dict['directory'], self.log_filepath))) "**[Software Info]**:\n\n {}".format(get_info(chat_env.env_dict['directory'], self.log_filepath)))
return chat_env return chat_env

BIN
misc/github.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB