From 0ff427a81ba92c9266b5a95bc3c9af61bd7efaf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E9=92=A6=E9=9B=A8?= <1820851049@qq.com> Date: Tue, 27 Feb 2024 15:53:10 +0800 Subject: [PATCH 01/11] refactor: save for temporary status, remain ErrorHandle Gate to be processed --- repo_agent/api_client.py | 43 +++++ repo_agent/chat_engine.py | 321 ++++++++++++++++++---------------- repo_agent/exceptions.py | 18 ++ repo_agent/project_manager.py | 35 ++++ 4 files changed, 265 insertions(+), 152 deletions(-) create mode 100644 repo_agent/api_client.py create mode 100644 repo_agent/exceptions.py diff --git a/repo_agent/api_client.py b/repo_agent/api_client.py new file mode 100644 index 0000000..79c6608 --- /dev/null +++ b/repo_agent/api_client.py @@ -0,0 +1,43 @@ +import requests +from requests.exceptions import RequestException +from time import sleep +from repo_agent.exceptions import APIError +""" +OpenAIAPIClient 类将负责与OpenAI API的所有通信,包括发送请求和处理响应。它还将处理API调用的重试逻辑。 +""" +class OpenAIAPIClient: + def __init__(self, api_key): + self.api_key = api_key + self.headers = { + 'Authorization': f'Bearer {self.api_key}', + 'Content-Type': 'application/json' + } + + def call_api(self, endpoint, payload, retries=3, backoff_factor=0.3): + """ + Call the OpenAI API with the given endpoint and payload. + Retries on failures with exponential backoff. + + Parameters: + endpoint (str): The API endpoint to call. + payload (dict): The payload to send in the API call. + retries (int): Number of times to retry on failure. + backoff_factor (float): Factor by which to increase delay between retries. + + Returns: + dict: The JSON response from the API. + + Raises: + APIError: An error occurred when calling the API. + """ + for attempt in range(retries): + try: + response = requests.post(endpoint, json=payload, headers=self.headers) + response.raise_for_status() + return response.json() + except RequestException as e: + if attempt < retries - 1: + sleep((2 ** attempt) * backoff_factor) + continue + else: + raise APIError(e, status_code=response.status_code if response else None) diff --git a/repo_agent/chat_engine.py b/repo_agent/chat_engine.py index a463efd..e70afeb 100644 --- a/repo_agent/chat_engine.py +++ b/repo_agent/chat_engine.py @@ -8,12 +8,8 @@ from repo_agent.prompt import SYS_PROMPT, USR_PROMPT from repo_agent.doc_meta_info import DocItem import inspect -from collections import defaultdict from repo_agent.log import logger - -class ContextLengthExceededError(Exception): - """Exception raised when the input size exceeds the model's context length limit.""" - pass +from repo_agent.api_client import OpenAIAPIClient def get_import_statements(): source_lines = inspect.getsourcelines(sys.modules[__name__])[0] @@ -24,36 +20,9 @@ def get_import_statements(): ] return import_lines - -def build_path_tree(who_reference_me, reference_who, doc_item_path): - def tree(): - return defaultdict(tree) - - path_tree = tree() - - for path_list in [who_reference_me, reference_who]: - for path in path_list: - parts = path.split(os.sep) - node = path_tree - for part in parts: - node = node[part] - - # 处理 doc_item_path - parts = doc_item_path.split(os.sep) - parts[-1] = "✳️" + parts[-1] # 在最后一个对象前面加上星号 - node = path_tree - for part in parts: - node = node[part] - - def tree_to_string(tree, indent=0): - s = "" - for key, value in sorted(tree.items()): - s += " " * indent + key + "\n" - if isinstance(value, dict): - s += tree_to_string(value, indent + 1) - return s - - return tree_to_string(path_tree) +class ResponseMessage: + def __init__(self, content): + self.content = content class ChatEngine: @@ -61,14 +30,120 @@ class ChatEngine: ChatEngine is used to generate the doc of functions or classes. """ - def __init__(self, CONFIG): + def __init__(self, CONFIG, project_manager): self.config = CONFIG + self.project_manager = project_manager + # self.api_client = OpenAIAPIClient(CONFIG['api_key']) def num_tokens_from_string(self, string: str, encoding_name="cl100k_base") -> int: """Returns the number of tokens in a text string.""" encoding = tiktoken.get_encoding(encoding_name) num_tokens = len(encoding.encode(string)) return num_tokens + + + def reduce_input_length(self, shorten_attempt, prompt_data): + """ + Reduces the length of the input prompts by modifying the sys_prompt contents. + """ + + logger.info(f"Attempt {shorten_attempt + 1} / 2 to reduce the length of the messages.") + if shorten_attempt == 0: + # First attempt, remove project_structure and project_structure_prefix + prompt_data.project_structure = "" + prompt_data.project_structure_prefix = "" + elif shorten_attempt == 1: + # Second attempt, futher remove caller and callee (reference) information + prompt_data.project_structure = "" + prompt_data.project_structure_prefix = "" + + prompt_data.referenced = False + prompt_data.referencer_content = "" + prompt_data.reference_letter = "" + prompt_data.combine_ref_situation = "" + + # Update sys_prompt + sys_prompt = SYS_PROMPT.format(**prompt_data) + + return sys_prompt + + + def generate_response(self, model, sys_prompt, usr_prompt, max_tokens): + """ + Generates a response from the OpenAI API based on the provided prompts and model. + If the response is not successful, it raises an OpenAIResponseError. + + Parameters: + model (str): The model to be used for the API request. + sys_prompt (str): The system prompt. + usr_prompt (str): The user prompt. + max_tokens (int): The maximum number of tokens allowed in the output. + + Returns: + str: The response message from the API. + + Raises: + OpenAIError: If an error occurs during the API call. + OpenAIResponseError: If the response message is None or not as expected. + """ + # Get basic configuration + client = OpenAI( + api_key=self.config["api_keys"][model][0]["api_key"], + base_url=self.config["api_keys"][model][0]["base_url"], + timeout=self.config["default_completion_kwargs"]["request_timeout"], + ) + + messages = [ + {"role": "system", "content": sys_prompt}, + {"role": "user", "content": usr_prompt}, + ] + + response = client.chat.completions.create( + model=model, + messages=messages, + temperature=self.config["default_completion_kwargs"]["temperature"], + max_tokens=max_tokens, + ) + + response_message = response.choices[0].message + + return response_message + + +def attempt_generate_response(self, model, sys_prompt, usr_prompt, max_tokens, max_attempts=5): + attempt = 0 + while attempt < max_attempts: + try: + response_message = self.generate_response(model, sys_prompt, usr_prompt, max_tokens) + + # 如果 response_message 是 None,则继续下一次循环 + if response_message is None: + attempt += 1 + continue + return response_message + + except APIConnectionError as e: + logger.error(f"Connection error: {e}. Attempt {attempt + 1} of {max_attempts}") + # Retry after 7 seconds + time.sleep(7) + attempt += 1 + if attempt == max_attempts: + raise + else: + continue # Try to request again + + except Exception as e: + logger.error( + f"An unknown error occurred: {e}. \nAttempt {attempt + 1} of {max_attempts}" + ) + # Retry after 10 seconds + time.sleep(10) + attempt += 1 + if attempt == max_attempts: + response_message = ResponseMessage("An unknown error occurred while generating this documentation after many tries.") + return response_message + + def generate_doc(self, doc_item: DocItem, file_handler): code_info = doc_item.content @@ -84,7 +159,8 @@ def generate_doc(self, doc_item: DocItem, file_handler): doc_item_path = file_path + "/" + code_name # 树结构路径通过全局信息中的who reference me 和 reference who + 自身的file_path来获取 - project_structure = build_path_tree( + # 使用 ProjectManager 实例来获取项目结构 + project_structure = self.project_manager.build_path_tree( who_reference_me, reference_who, doc_item_path ) @@ -165,22 +241,24 @@ def get_relationship_description(referencer_content, reference_letter): project_structure_prefix = ", and the related hierarchical structure of this project is as follows (The current object is marked with an *):" - sys_prompt = SYS_PROMPT.format( - combine_ref_situation=combine_ref_situation, - file_path=file_path, - project_structure_prefix=project_structure_prefix, - project_structure=project_structure, - code_type_tell=code_type_tell, - code_name=code_name, - code_content=code_content, - have_return_tell=have_return_tell, - # referenced=referenced, - has_relationship=has_relationship, - reference_letter=reference_letter, - referencer_content=referencer_content, - parameters_or_attribute=parameters_or_attribute, - language=language, - ) + # 第一次尝试构建完整的prompt + prompt_data = { + "combine_ref_situation": combine_ref_situation, + "file_path": file_path, + "project_structure_prefix": project_structure_prefix, + "project_structure": project_structure, + "code_type_tell": code_type_tell, + "code_name": code_name, + "code_content": code_content, + "have_return_tell": have_return_tell, + "has_relationship": has_relationship, + "reference_letter": reference_letter, + "referencer_content": referencer_content, + "parameters_or_attribute": parameters_or_attribute, + "language": language, + } + + sys_prompt = SYS_PROMPT.format(**prompt_data) usr_prompt = USR_PROMPT.format(language=language) @@ -201,104 +279,43 @@ def get_relationship_description(referencer_content, reference_letter): # 如果总tokens超过当前模型的限制,则尝试寻找较大模型或者缩减输入 if total_tokens >= max_input_length: # 查找一个拥有更大输入限制的模型 - larger_models = {k: v for k, v in max_input_tokens_map.items() if (v-max_tokens) > max_input_length} - if larger_models: - # 选择一个拥有更大输入限制的模型 - model = max(larger_models, key=larger_models.get) - logger.info(f"Switching to {model} for long-context processing.") - else: - for attempt in range(2): - logger.info(f"Attempt {attempt + 1} of {max_attempts}: Reducing the length of the messages.") - if attempt == 0: - # 第一次尝试,移除 project_structure 和 project_structure_prefix - project_structure = "" - project_structure_prefix = "" - elif attempt == 1: - # 第二次尝试,移除相关的调用者和被调用者信息 - referenced = False - referencer_content = "" - reference_letter = "" - combine_ref_situation = "" - - # 更新 sys_prompt - sys_prompt = SYS_PROMPT.format( - reference_letter=reference_letter, - combine_ref_situation=combine_ref_situation, - file_path=file_path, - project_structure_prefix=project_structure_prefix, - project_structure=project_structure, - code_type_tell=code_type_tell, - code_name=code_name, - code_content=code_content, - have_return_tell=have_return_tell, - has_relationship=has_relationship, - referenced=referenced, - referencer_content=referencer_content, - parameters_or_attribute=parameters_or_attribute, - language=language, - ) - - # 重新计算 tokens - total_tokens = ( - self.num_tokens_from_string(sys_prompt) + - self.num_tokens_from_string(usr_prompt) - ) - # 检查是否满足要求 - if total_tokens < max_input_length: - break - - if total_tokens >= max_input_length: - error_message = ( - f"Context length of {total_tokens} exceeds the maximum limit of {max_input_length} tokens..." - ) - # raise ContextLengthExceededError(error_message) - return None - - attempt = 0 - while attempt < max_attempts: - try: - # 获取基本配置 - client = OpenAI( - api_key=self.config["api_keys"][model][0]["api_key"], - base_url=self.config["api_keys"][model][0]["base_url"], - timeout=self.config["default_completion_kwargs"]["request_timeout"], + larger_models = {k: v for k, v in max_input_tokens_map.items() if (v - max_tokens) > total_tokens} # 抽取出所有上下文长度大于当前总输入tokens的模型 + for model_name, max_input_length in larger_models: + if max_input_length - max_tokens > total_tokens: + try: + # Attempt to make a request with the larger model + logger.info(f"Trying model {model_name} for large-context processing.") + response_message = self.attempt_generate_response(model, sys_prompt, usr_prompt, max_tokens) # response_message在attempt_generate_response中已经被校验过了 + + except Exception as e: + # 否值直接跳过,尝试下一个模型 + # logger.error(f"Model {model_name} failed with error: {e}") + continue # Try the next model + # If no larger models succeed, fallback to original model + # 对于最初的model模型,尝试缩减输入长度 + for shorten_attempt in range(2): + shorten_success = False + sys_prompt = self.reduce_input_length(shorten_attempt, prompt_data) + # 重新计算 tokens + total_tokens = ( + self.num_tokens_from_string(sys_prompt) + + self.num_tokens_from_string(usr_prompt) ) - - messages = [ - {"role": "system", "content": sys_prompt}, - {"role": "user", "content": usr_prompt}, - ] - - response = client.chat.completions.create( - model=model, - messages=messages, - temperature=self.config["default_completion_kwargs"]["temperature"], - max_tokens=max_tokens, - ) - - response_message = response.choices[0].message - - # 如果 response_message 是 None,则继续下一次循环 - if response_message is None: - attempt += 1 - continue + # 检查是否满足要求 + if total_tokens < max_input_length: + shorten_success = True + # 如满足要求直接发送请求来生成文档 + response_message = self.attempt_generate_response(model, sys_prompt, usr_prompt, max_tokens) + + + if not shorten_success: + # 意味着这个doc_item无法生成doc(因为代码本身的长度就超过了模型的限制) + # 返回一个自定义的response_message对象,它的content是"Tried to generate the document, but the code is too long to process." + # 在其他代码调用的时候使用的是response_message.content,所以必须确保content能通过这种方式从response_message中被读取出来 + response_message = ResponseMessage("Tried to generate the document, but the code is too long to process.") return response_message - - except APIConnectionError as e: - logger.error(f"Connection error: {e}. Attempt {attempt + 1} of {max_attempts}") - # Retry after 7 seconds - time.sleep(7) - attempt += 1 - if attempt == max_attempts: - raise - else: - continue # Try to request again - except Exception as e: - logger.error( - f"An unknown error occurred: {e}. \nAttempt {attempt + 1} of {max_attempts}" - ) - # Retry after 10 seconds - time.sleep(10) - attempt += 1 - if attempt == max_attempts: - return None + + else: # 如果总tokens没有超过模型限制,直接发送请求 + response_message = self.attempt_generate_response(model, sys_prompt, usr_prompt, max_tokens) + + return response_message diff --git a/repo_agent/exceptions.py b/repo_agent/exceptions.py new file mode 100644 index 0000000..287a716 --- /dev/null +++ b/repo_agent/exceptions.py @@ -0,0 +1,18 @@ +from openai import APIConnectionError +from repo_agent.log import logger +class ErrorHandler: + @staticmethod + def handle_exception(e): + if isinstance(e, APIConnectionError): + logger.warning(f"OpenAIResponseError occurred: {e}") + # Add your handling code here + elif isinstance(e, OpenAIError): + print(f"OpenAIError occurred: {e}") + # Add your handling code here + else: + print(f"An unexpected error occurred: {e}") + # Add your handling code here + +class OpenAIError(Exception): + def __init__(self, message): + super().__init__(message) \ No newline at end of file diff --git a/repo_agent/project_manager.py b/repo_agent/project_manager.py index 6a07a11..0b67474 100644 --- a/repo_agent/project_manager.py +++ b/repo_agent/project_manager.py @@ -1,5 +1,7 @@ import os import jedi +from collections import defaultdict + class ProjectManager: @@ -32,6 +34,39 @@ def walk_dir(root, prefix=""): structure = [] walk_dir(self.repo_path) return "\n".join(structure) + + def build_path_tree(self, who_reference_me, reference_who, doc_item_path): + from collections import defaultdict + + def tree(): + return defaultdict(tree) + + path_tree = tree() + + # 构建 who_reference_me 和 reference_who 的树 + for path_list in [who_reference_me, reference_who]: + for path in path_list: + parts = path.split(os.sep) + node = path_tree + for part in parts: + node = node[part] + + # 处理 doc_item_path + parts = doc_item_path.split(os.sep) + parts[-1] = "✳️" + parts[-1] # 在最后一个对象前面加上星号 + node = path_tree + for part in parts: + node = node[part] + + def tree_to_string(tree, indent=0): + s = "" + for key, value in sorted(tree.items()): + s += " " * indent + key + "\n" + if isinstance(value, dict): + s += tree_to_string(value, indent + 1) + return s + + return tree_to_string(path_tree) # def find_all_referencer(self, variable_name, file_path, line_number, column_number): # """ From 5d8cf712e24a968dbd77040a861c996aafc9920b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E9=92=A6=E9=9B=A8?= <1820851049@qq.com> Date: Wed, 28 Feb 2024 22:43:23 +0800 Subject: [PATCH 02/11] fix: adjust format problem --- repo_agent/chat_engine.py | 400 +++++++++++++++++++------------------- 1 file changed, 200 insertions(+), 200 deletions(-) diff --git a/repo_agent/chat_engine.py b/repo_agent/chat_engine.py index e70afeb..6b1c202 100644 --- a/repo_agent/chat_engine.py +++ b/repo_agent/chat_engine.py @@ -110,212 +110,212 @@ def generate_response(self, model, sys_prompt, usr_prompt, max_tokens): return response_message -def attempt_generate_response(self, model, sys_prompt, usr_prompt, max_tokens, max_attempts=5): - attempt = 0 - while attempt < max_attempts: - try: - response_message = self.generate_response(model, sys_prompt, usr_prompt, max_tokens) - - # 如果 response_message 是 None,则继续下一次循环 - if response_message is None: - attempt += 1 - continue - return response_message - - except APIConnectionError as e: - logger.error(f"Connection error: {e}. Attempt {attempt + 1} of {max_attempts}") - # Retry after 7 seconds - time.sleep(7) - attempt += 1 - if attempt == max_attempts: - raise - else: - continue # Try to request again - - except Exception as e: - logger.error( - f"An unknown error occurred: {e}. \nAttempt {attempt + 1} of {max_attempts}" - ) - # Retry after 10 seconds - time.sleep(10) - attempt += 1 - if attempt == max_attempts: - response_message = ResponseMessage("An unknown error occurred while generating this documentation after many tries.") + def attempt_generate_response(self, model, sys_prompt, usr_prompt, max_tokens, max_attempts=5): + attempt = 0 + while attempt < max_attempts: + try: + response_message = self.generate_response(model, sys_prompt, usr_prompt, max_tokens) + + # 如果 response_message 是 None,则继续下一次循环 + if response_message is None: + attempt += 1 + continue return response_message - - - def generate_doc(self, doc_item: DocItem, file_handler): - code_info = doc_item.content - referenced = len(doc_item.who_reference_me) > 0 - - code_type = code_info["type"] - code_name = code_info["name"] - code_content = code_info["code_content"] - have_return = code_info["have_return"] - who_reference_me = doc_item.who_reference_me_name_list - reference_who = doc_item.reference_who_name_list - file_path = doc_item.get_full_name() - doc_item_path = file_path + "/" + code_name - - # 树结构路径通过全局信息中的who reference me 和 reference who + 自身的file_path来获取 - # 使用 ProjectManager 实例来获取项目结构 - project_structure = self.project_manager.build_path_tree( - who_reference_me, reference_who, doc_item_path - ) - - # project_manager = ProjectManager(repo_path=file_handler.repo_path, project_hierarchy=file_handler.project_hierarchy) - # project_structure = project_manager.get_project_structure() - # file_path = os.path.join(file_handler.repo_path, file_handler.file_path) - # code_from_referencer = get_code_from_json(project_manager.project_hierarchy, referencer) # - # referenced = True if len(code_from_referencer) > 0 else False - # referencer_content = '\n'.join([f'File_Path:{file_path}\n' + '\n'.join([f'Corresponding code as follows:\n{code}\n[End of this part of code]' for code in codes]) + f'\n[End of {file_path}]' for file_path, codes in code_from_referencer.items()]) - - def get_referenced_prompt(doc_item: DocItem) -> str: - if len(doc_item.reference_who) == 0: - return "" - prompt = [ - """As you can see, the code calls the following objects, their code and docs are as following:""" - ] - for k, reference_item in enumerate(doc_item.reference_who): - instance_prompt = ( - f"""obj: {reference_item.get_full_name()}\nDocument: \n{reference_item.md_content[-1] if len(reference_item.md_content) > 0 else 'None'}\nRaw code:```\n{reference_item.content['code_content'] if 'code_content' in reference_item.content.keys() else ''}\n```""" - + "=" * 10 - ) - prompt.append(instance_prompt) - return "\n".join(prompt) - - def get_referencer_prompt(doc_item: DocItem) -> str: - if len(doc_item.who_reference_me) == 0: - return "" - prompt = [ - """Also, the code has been called by the following objects, their code and docs are as following:""" - ] - for k, referencer_item in enumerate(doc_item.who_reference_me): - instance_prompt = ( - f"""obj: {referencer_item.get_full_name()}\nDocument: \n{referencer_item.md_content[-1] if len(referencer_item.md_content) > 0 else 'None'}\nRaw code:```\n{referencer_item.content['code_content'] if 'code_content' in referencer_item.content.keys() else 'None'}\n```""" - + "=" * 10 + except APIConnectionError as e: + logger.error(f"Connection error: {e}. Attempt {attempt + 1} of {max_attempts}") + # Retry after 7 seconds + time.sleep(7) + attempt += 1 + if attempt == max_attempts: + raise + else: + continue # Try to request again + + except Exception as e: + logger.error( + f"An unknown error occurred: {e}. \nAttempt {attempt + 1} of {max_attempts}" ) - prompt.append(instance_prompt) - return "\n".join(prompt) - - def get_relationship_description(referencer_content, reference_letter): - if referencer_content and reference_letter: - has_relationship = "And please include the reference relationship with its callers and callees in the project from a functional perspective" - elif referencer_content: - return "And please include the relationship with its callers in the project from a functional perspective." - elif reference_letter: - return "And please include the relationship with its callees in the project from a functional perspective." - else: - return "" + # Retry after 10 seconds + time.sleep(10) + attempt += 1 + if attempt == max_attempts: + response_message = ResponseMessage("An unknown error occurred while generating this documentation after many tries.") + return response_message - max_tokens = self.config.get("max_document_tokens", 1024) or 1024 - max_attempts = 5 # 设置最大尝试次数 - language = self.config["language"] # setting document language - if language not in language_mapping: - raise KeyError( - f"Language code {language} is not provided! Supported languages are: {json.dumps(language_mapping)}" - ) - language = language_mapping[language] - - code_type_tell = "Class" if code_type == "ClassDef" else "Function" - parameters_or_attribute = ( - "attributes" if code_type == "ClassDef" else "parameters" - ) - have_return_tell = ( - "**Output Example**: Mock up a possible appearance of the code's return value." - if have_return - else "" - ) - # reference_letter = "This object is called in the following files, the file paths and corresponding calling parts of the code are as follows:" if referenced else "" - combine_ref_situation = ( - "and combine it with its calling situation in the project," - if referenced - else "" - ) - - referencer_content = get_referencer_prompt(doc_item) - reference_letter = get_referenced_prompt(doc_item) - has_relationship = get_relationship_description(referencer_content, reference_letter) - - project_structure_prefix = ", and the related hierarchical structure of this project is as follows (The current object is marked with an *):" - - # 第一次尝试构建完整的prompt - prompt_data = { - "combine_ref_situation": combine_ref_situation, - "file_path": file_path, - "project_structure_prefix": project_structure_prefix, - "project_structure": project_structure, - "code_type_tell": code_type_tell, - "code_name": code_name, - "code_content": code_content, - "have_return_tell": have_return_tell, - "has_relationship": has_relationship, - "reference_letter": reference_letter, - "referencer_content": referencer_content, - "parameters_or_attribute": parameters_or_attribute, - "language": language, - } - - sys_prompt = SYS_PROMPT.format(**prompt_data) - - usr_prompt = USR_PROMPT.format(language=language) - - # # 保存prompt到txt文件 - # with open(f'prompt_output/sys_prompt_{code_name}.txt', 'w', encoding='utf-8') as f: - # f.write(sys_prompt+'\n'+ usr_prompt) - # logger.info(f"Using {max_input_tokens_map} for context window judgment.") - - model = self.config["default_completion_kwargs"]["model"] - max_input_length = max_input_tokens_map.get(model, 4096) - max_tokens +def generate_doc(self, doc_item: DocItem, file_handler): + code_info = doc_item.content + referenced = len(doc_item.who_reference_me) > 0 + + code_type = code_info["type"] + code_name = code_info["name"] + code_content = code_info["code_content"] + have_return = code_info["have_return"] + who_reference_me = doc_item.who_reference_me_name_list + reference_who = doc_item.reference_who_name_list + file_path = doc_item.get_full_name() + doc_item_path = file_path + "/" + code_name + + # 树结构路径通过全局信息中的who reference me 和 reference who + 自身的file_path来获取 + # 使用 ProjectManager 实例来获取项目结构 + project_structure = self.project_manager.build_path_tree( + who_reference_me, reference_who, doc_item_path + ) + + # project_manager = ProjectManager(repo_path=file_handler.repo_path, project_hierarchy=file_handler.project_hierarchy) + # project_structure = project_manager.get_project_structure() + # file_path = os.path.join(file_handler.repo_path, file_handler.file_path) + # code_from_referencer = get_code_from_json(project_manager.project_hierarchy, referencer) # + # referenced = True if len(code_from_referencer) > 0 else False + # referencer_content = '\n'.join([f'File_Path:{file_path}\n' + '\n'.join([f'Corresponding code as follows:\n{code}\n[End of this part of code]' for code in codes]) + f'\n[End of {file_path}]' for file_path, codes in code_from_referencer.items()]) + + def get_referenced_prompt(doc_item: DocItem) -> str: + if len(doc_item.reference_who) == 0: + return "" + prompt = [ + """As you can see, the code calls the following objects, their code and docs are as following:""" + ] + for k, reference_item in enumerate(doc_item.reference_who): + instance_prompt = ( + f"""obj: {reference_item.get_full_name()}\nDocument: \n{reference_item.md_content[-1] if len(reference_item.md_content) > 0 else 'None'}\nRaw code:```\n{reference_item.content['code_content'] if 'code_content' in reference_item.content.keys() else ''}\n```""" + + "=" * 10 + ) + prompt.append(instance_prompt) + return "\n".join(prompt) + + def get_referencer_prompt(doc_item: DocItem) -> str: + if len(doc_item.who_reference_me) == 0: + return "" + prompt = [ + """Also, the code has been called by the following objects, their code and docs are as following:""" + ] + for k, referencer_item in enumerate(doc_item.who_reference_me): + instance_prompt = ( + f"""obj: {referencer_item.get_full_name()}\nDocument: \n{referencer_item.md_content[-1] if len(referencer_item.md_content) > 0 else 'None'}\nRaw code:```\n{referencer_item.content['code_content'] if 'code_content' in referencer_item.content.keys() else 'None'}\n```""" + + "=" * 10 + ) + prompt.append(instance_prompt) + return "\n".join(prompt) + + def get_relationship_description(referencer_content, reference_letter): + if referencer_content and reference_letter: + has_relationship = "And please include the reference relationship with its callers and callees in the project from a functional perspective" + elif referencer_content: + return "And please include the relationship with its callers in the project from a functional perspective." + elif reference_letter: + return "And please include the relationship with its callees in the project from a functional perspective." + else: + return "" - total_tokens = ( - self.num_tokens_from_string(sys_prompt) + - self.num_tokens_from_string(usr_prompt) + + max_tokens = self.config.get("max_document_tokens", 1024) or 1024 + max_attempts = 5 # 设置最大尝试次数 + language = self.config["language"] # setting document language + if language not in language_mapping: + raise KeyError( + f"Language code {language} is not provided! Supported languages are: {json.dumps(language_mapping)}" ) - - # 如果总tokens超过当前模型的限制,则尝试寻找较大模型或者缩减输入 - if total_tokens >= max_input_length: - # 查找一个拥有更大输入限制的模型 - larger_models = {k: v for k, v in max_input_tokens_map.items() if (v - max_tokens) > total_tokens} # 抽取出所有上下文长度大于当前总输入tokens的模型 - for model_name, max_input_length in larger_models: - if max_input_length - max_tokens > total_tokens: - try: - # Attempt to make a request with the larger model - logger.info(f"Trying model {model_name} for large-context processing.") - response_message = self.attempt_generate_response(model, sys_prompt, usr_prompt, max_tokens) # response_message在attempt_generate_response中已经被校验过了 - - except Exception as e: - # 否值直接跳过,尝试下一个模型 - # logger.error(f"Model {model_name} failed with error: {e}") - continue # Try the next model - # If no larger models succeed, fallback to original model - # 对于最初的model模型,尝试缩减输入长度 - for shorten_attempt in range(2): - shorten_success = False - sys_prompt = self.reduce_input_length(shorten_attempt, prompt_data) - # 重新计算 tokens - total_tokens = ( - self.num_tokens_from_string(sys_prompt) + - self.num_tokens_from_string(usr_prompt) - ) - # 检查是否满足要求 - if total_tokens < max_input_length: - shorten_success = True - # 如满足要求直接发送请求来生成文档 - response_message = self.attempt_generate_response(model, sys_prompt, usr_prompt, max_tokens) - - - if not shorten_success: - # 意味着这个doc_item无法生成doc(因为代码本身的长度就超过了模型的限制) - # 返回一个自定义的response_message对象,它的content是"Tried to generate the document, but the code is too long to process." - # 在其他代码调用的时候使用的是response_message.content,所以必须确保content能通过这种方式从response_message中被读取出来 - response_message = ResponseMessage("Tried to generate the document, but the code is too long to process.") - return response_message - - else: # 如果总tokens没有超过模型限制,直接发送请求 - response_message = self.attempt_generate_response(model, sys_prompt, usr_prompt, max_tokens) + language = language_mapping[language] + + code_type_tell = "Class" if code_type == "ClassDef" else "Function" + parameters_or_attribute = ( + "attributes" if code_type == "ClassDef" else "parameters" + ) + have_return_tell = ( + "**Output Example**: Mock up a possible appearance of the code's return value." + if have_return + else "" + ) + # reference_letter = "This object is called in the following files, the file paths and corresponding calling parts of the code are as follows:" if referenced else "" + combine_ref_situation = ( + "and combine it with its calling situation in the project," + if referenced + else "" + ) + + referencer_content = get_referencer_prompt(doc_item) + reference_letter = get_referenced_prompt(doc_item) + has_relationship = get_relationship_description(referencer_content, reference_letter) + + project_structure_prefix = ", and the related hierarchical structure of this project is as follows (The current object is marked with an *):" + + # 第一次尝试构建完整的prompt + prompt_data = { + "combine_ref_situation": combine_ref_situation, + "file_path": file_path, + "project_structure_prefix": project_structure_prefix, + "project_structure": project_structure, + "code_type_tell": code_type_tell, + "code_name": code_name, + "code_content": code_content, + "have_return_tell": have_return_tell, + "has_relationship": has_relationship, + "reference_letter": reference_letter, + "referencer_content": referencer_content, + "parameters_or_attribute": parameters_or_attribute, + "language": language, + } + + sys_prompt = SYS_PROMPT.format(**prompt_data) + + usr_prompt = USR_PROMPT.format(language=language) + + # # 保存prompt到txt文件 + # with open(f'prompt_output/sys_prompt_{code_name}.txt', 'w', encoding='utf-8') as f: + # f.write(sys_prompt+'\n'+ usr_prompt) + + # logger.info(f"Using {max_input_tokens_map} for context window judgment.") + + model = self.config["default_completion_kwargs"]["model"] + max_input_length = max_input_tokens_map.get(model, 4096) - max_tokens + + total_tokens = ( + self.num_tokens_from_string(sys_prompt) + + self.num_tokens_from_string(usr_prompt) + ) + + # 如果总tokens超过当前模型的限制,则尝试寻找较大模型或者缩减输入 + if total_tokens >= max_input_length: + # 查找一个拥有更大输入限制的模型 + larger_models = {k: v for k, v in max_input_tokens_map.items() if (v - max_tokens) > total_tokens} # 抽取出所有上下文长度大于当前总输入tokens的模型 + for model_name, max_input_length in larger_models: + if max_input_length - max_tokens > total_tokens: + try: + # Attempt to make a request with the larger model + logger.info(f"Trying model {model_name} for large-context processing.") + response_message = self.attempt_generate_response(model_name, sys_prompt, usr_prompt, max_tokens) # response_message在attempt_generate_response中已经被校验过了 + + except Exception as e: + # 否值直接跳过,尝试下一个模型 + # logger.error(f"Model {model_name} failed with error: {e}") + continue # Try the next model + # If no larger models succeed, fallback to original model + # 对于最初的model模型,尝试缩减输入长度 + for shorten_attempt in range(2): + shorten_success = False + sys_prompt = self.reduce_input_length(shorten_attempt, prompt_data) + # 重新计算 tokens + total_tokens = ( + self.num_tokens_from_string(sys_prompt) + + self.num_tokens_from_string(usr_prompt) + ) + # 检查是否满足要求 + if total_tokens < max_input_length: + shorten_success = True + # 如满足要求直接发送请求来生成文档 + response_message = self.attempt_generate_response(model, sys_prompt, usr_prompt, max_tokens) + + + if not shorten_success: + # 意味着这个doc_item无法生成doc(因为代码本身的长度就超过了模型的限制) + # 返回一个自定义的response_message对象,它的content是"Tried to generate the document, but the code is too long to process." + # 在其他代码调用的时候使用的是response_message.content,所以必须确保content能通过这种方式从response_message中被读取出来 + response_message = ResponseMessage("Tried to generate the document, but the code is too long to process.") + return response_message - return response_message + else: # 如果总tokens没有超过模型限制,直接发送请求 + response_message = self.attempt_generate_response(model, sys_prompt, usr_prompt, max_tokens) + + return response_message From d60546ba4fecfe9a10cc7e545dbb9e130b0239b2 Mon Sep 17 00:00:00 2001 From: Umpire2018 <138990495+Umpire2018@users.noreply.github.com> Date: Thu, 29 Feb 2024 02:49:07 +0000 Subject: [PATCH 03/11] fix: incorrect indentation in `chat_engine.py` - Update dependencies to the latest versions - Remove unused content in log.py - Delete unused api_client.py - Remove unused import statements --- pdm.lock | 428 ++++++++++++++++++------------------ repo_agent/api_client.py | 43 ---- repo_agent/chat_engine.py | 409 +++++++++++++++++----------------- repo_agent/doc_meta_info.py | 1 - repo_agent/exceptions.py | 9 +- repo_agent/file_handler.py | 7 +- repo_agent/log.py | 57 ----- requirements.txt | 378 +++++++++++++++---------------- 8 files changed, 607 insertions(+), 725 deletions(-) delete mode 100644 repo_agent/api_client.py diff --git a/pdm.lock b/pdm.lock index 24e78fc..160ab8e 100644 --- a/pdm.lock +++ b/pdm.lock @@ -246,13 +246,13 @@ files = [ [[package]] name = "cachetools" -version = "5.3.2" +version = "5.3.3" requires_python = ">=3.7" summary = "Extensible memoizing collections and decorators" groups = ["default"] files = [ - {file = "cachetools-5.3.2-py3-none-any.whl", hash = "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1"}, - {file = "cachetools-5.3.2.tar.gz", hash = "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2"}, + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, ] [[package]] @@ -346,7 +346,7 @@ files = [ [[package]] name = "chromadb" -version = "0.4.23" +version = "0.4.24" requires_python = ">=3.8" summary = "Chroma." groups = ["default"] @@ -381,8 +381,8 @@ dependencies = [ "uvicorn[standard]>=0.18.3", ] files = [ - {file = "chromadb-0.4.23-py3-none-any.whl", hash = "sha256:3d3c2ffb4ff560721e3daf8c1a3729fd149c551525b6f75543eddb81a4f29e16"}, - {file = "chromadb-0.4.23.tar.gz", hash = "sha256:54d9a770640704c6cedc15317faab9fd45beb9833e7484c00037e7a8801a349f"}, + {file = "chromadb-0.4.24-py3-none-any.whl", hash = "sha256:3a08e237a4ad28b5d176685bd22429a03717fe09d35022fb230d516108da01da"}, + {file = "chromadb-0.4.24.tar.gz", hash = "sha256:a5c80b4e4ad9b236ed2d4899a5b9e8002b489293f2881cb2cadab5b199ee1c72"}, ] [[package]] @@ -545,7 +545,7 @@ files = [ [[package]] name = "fastapi" -version = "0.109.2" +version = "0.110.0" requires_python = ">=3.8" summary = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" groups = ["default"] @@ -555,8 +555,8 @@ dependencies = [ "typing-extensions>=4.8.0", ] files = [ - {file = "fastapi-0.109.2-py3-none-any.whl", hash = "sha256:2c9bab24667293b501cad8dd388c05240c850b58ec5876ee3283c47d6e1e3a4d"}, - {file = "fastapi-0.109.2.tar.gz", hash = "sha256:f3817eac96fe4f65a2ebb4baa000f394e55f5fccdaf7f75250804bc58f354f73"}, + {file = "fastapi-0.110.0-py3-none-any.whl", hash = "sha256:87a1f6fb632a218222c5984be540055346a8f5d8a68e8f6fb647b1dc9934de4b"}, + {file = "fastapi-0.110.0.tar.gz", hash = "sha256:266775f0dcc95af9d3ef39bad55cff525329a931d5fd51930aadd4f428bf7ff3"}, ] [[package]] @@ -958,7 +958,7 @@ files = [ [[package]] name = "huggingface-hub" -version = "0.20.3" +version = "0.21.2" requires_python = ">=3.8.0" summary = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" groups = ["default"] @@ -972,8 +972,8 @@ dependencies = [ "typing-extensions>=3.7.4.3", ] files = [ - {file = "huggingface_hub-0.20.3-py3-none-any.whl", hash = "sha256:d988ae4f00d3e307b0c80c6a05ca6dbb7edba8bba3079f74cda7d9c2e562a7b6"}, - {file = "huggingface_hub-0.20.3.tar.gz", hash = "sha256:94e7f8e074475fbc67d6a71957b678e1b4a74ff1b64a644fd6cbb83da962d05d"}, + {file = "huggingface_hub-0.21.2-py3-none-any.whl", hash = "sha256:16955c2b60bcff32a0778f84b9e9ae8f61d7f003da6aa1fbb7bc897a0c37b28c"}, + {file = "huggingface_hub-0.21.2.tar.gz", hash = "sha256:839f2fc69fc51797b76dcffa7edbf7fb1150176f74cb1dc2d87ca00e5e0b5611"}, ] [[package]] @@ -1017,13 +1017,13 @@ files = [ [[package]] name = "importlib-resources" -version = "6.1.1" +version = "6.1.2" requires_python = ">=3.8" summary = "Read resources from Python packages" groups = ["default"] files = [ - {file = "importlib_resources-6.1.1-py3-none-any.whl", hash = "sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6"}, - {file = "importlib_resources-6.1.1.tar.gz", hash = "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a"}, + {file = "importlib_resources-6.1.2-py3-none-any.whl", hash = "sha256:9a0a862501dc38b68adebc82970140c9e4209fc99601782925178f8386339938"}, + {file = "importlib_resources-6.1.2.tar.gz", hash = "sha256:308abf8474e2dba5f867d279237cd4076482c3de7104a40b41426370e891549b"}, ] [[package]] @@ -1313,7 +1313,7 @@ files = [ [[package]] name = "marshmallow" -version = "3.20.2" +version = "3.21.0" requires_python = ">=3.8" summary = "A lightweight library for converting complex datatypes to and from native Python datatypes." groups = ["default"] @@ -1321,8 +1321,8 @@ dependencies = [ "packaging>=17.0", ] files = [ - {file = "marshmallow-3.20.2-py3-none-any.whl", hash = "sha256:c21d4b98fee747c130e6bc8f45c4b3199ea66bc00c12ee1f639f0aeca034d5e9"}, - {file = "marshmallow-3.20.2.tar.gz", hash = "sha256:4c1daff273513dc5eb24b219a8035559dc573c8f322558ef85f5438ddd1236dd"}, + {file = "marshmallow-3.21.0-py3-none-any.whl", hash = "sha256:e7997f83571c7fd476042c2c188e4ee8a78900ca5e74bd9c8097afa56624e9bd"}, + {file = "marshmallow-3.21.0.tar.gz", hash = "sha256:20f53be28c6e374a711a16165fb22a8dc6003e3f7cda1285e3ca777b9193885b"}, ] [[package]] @@ -1611,7 +1611,7 @@ files = [ [[package]] name = "onnxruntime" -version = "1.17.0" +version = "1.17.1" summary = "ONNX Runtime is a runtime accelerator for Machine Learning models" groups = ["default"] dependencies = [ @@ -1623,26 +1623,26 @@ dependencies = [ "sympy", ] files = [ - {file = "onnxruntime-1.17.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d2b22a25a94109cc983443116da8d9805ced0256eb215c5e6bc6dcbabefeab96"}, - {file = "onnxruntime-1.17.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4c87d83c6f58d1af2675fc99e3dc810f2dbdb844bcefd0c1b7573632661f6fc"}, - {file = "onnxruntime-1.17.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dba55723bf9b835e358f48c98a814b41692c393eb11f51e02ece0625c756b797"}, - {file = "onnxruntime-1.17.0-cp310-cp310-win32.whl", hash = "sha256:ee48422349cc500273beea7607e33c2237909f58468ae1d6cccfc4aecd158565"}, - {file = "onnxruntime-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f34cc46553359293854e38bdae2ab1be59543aad78a6317e7746d30e311110c3"}, - {file = "onnxruntime-1.17.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:16d26badd092c8c257fa57c458bb600d96dc15282c647ccad0ed7b2732e6c03b"}, - {file = "onnxruntime-1.17.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6f1273bebcdb47ed932d076c85eb9488bc4768fcea16d5f2747ca692fad4f9d3"}, - {file = "onnxruntime-1.17.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cb60fd3c2c1acd684752eb9680e89ae223e9801a9b0e0dc7b28adabe45a2e380"}, - {file = "onnxruntime-1.17.0-cp311-cp311-win32.whl", hash = "sha256:4b038324586bc905299e435f7c00007e6242389c856b82fe9357fdc3b1ef2bdc"}, - {file = "onnxruntime-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:93d39b3fa1ee01f034f098e1c7769a811a21365b4883f05f96c14a2b60c6028b"}, - {file = "onnxruntime-1.17.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:90c0890e36f880281c6c698d9bc3de2afbeee2f76512725ec043665c25c67d21"}, - {file = "onnxruntime-1.17.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7466724e809a40e986b1637cba156ad9fc0d1952468bc00f79ef340bc0199552"}, - {file = "onnxruntime-1.17.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d47bee7557a8b99c8681b6882657a515a4199778d6d5e24e924d2aafcef55b0a"}, - {file = "onnxruntime-1.17.0-cp312-cp312-win32.whl", hash = "sha256:bb1bf1ee575c665b8bbc3813ab906e091a645a24ccc210be7932154b8260eca1"}, - {file = "onnxruntime-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:ac2f286da3494b29b4186ca193c7d4e6a2c1f770c4184c7192c5da142c3dec28"}, + {file = "onnxruntime-1.17.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d43ac17ac4fa3c9096ad3c0e5255bb41fd134560212dc124e7f52c3159af5d21"}, + {file = "onnxruntime-1.17.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55b5e92a4c76a23981c998078b9bf6145e4fb0b016321a8274b1607bd3c6bd35"}, + {file = "onnxruntime-1.17.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ebbcd2bc3a066cf54e6f18c75708eb4d309ef42be54606d22e5bdd78afc5b0d7"}, + {file = "onnxruntime-1.17.1-cp310-cp310-win32.whl", hash = "sha256:5e3716b5eec9092e29a8d17aab55e737480487deabfca7eac3cd3ed952b6ada9"}, + {file = "onnxruntime-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:fbb98cced6782ae1bb799cc74ddcbbeeae8819f3ad1d942a74d88e72b6511337"}, + {file = "onnxruntime-1.17.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:36fd6f87a1ecad87e9c652e42407a50fb305374f9a31d71293eb231caae18784"}, + {file = "onnxruntime-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99a8bddeb538edabc524d468edb60ad4722cff8a49d66f4e280c39eace70500b"}, + {file = "onnxruntime-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd7fddb4311deb5a7d3390cd8e9b3912d4d963efbe4dfe075edbaf18d01c024e"}, + {file = "onnxruntime-1.17.1-cp311-cp311-win32.whl", hash = "sha256:606a7cbfb6680202b0e4f1890881041ffc3ac6e41760a25763bd9fe146f0b335"}, + {file = "onnxruntime-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:53e4e06c0a541696ebdf96085fd9390304b7b04b748a19e02cf3b35c869a1e76"}, + {file = "onnxruntime-1.17.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:40f08e378e0f85929712a2b2c9b9a9cc400a90c8a8ca741d1d92c00abec60843"}, + {file = "onnxruntime-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac79da6d3e1bb4590f1dad4bb3c2979d7228555f92bb39820889af8b8e6bd472"}, + {file = "onnxruntime-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ae9ba47dc099004e3781f2d0814ad710a13c868c739ab086fc697524061695ea"}, + {file = "onnxruntime-1.17.1-cp312-cp312-win32.whl", hash = "sha256:2dff1a24354220ac30e4a4ce2fb1df38cb1ea59f7dac2c116238d63fe7f4c5ff"}, + {file = "onnxruntime-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:6226a5201ab8cafb15e12e72ff2a4fc8f50654e8fa5737c6f0bd57c5ff66827e"}, ] [[package]] name = "openai" -version = "1.12.0" +version = "1.13.3" requires_python = ">=3.7.1" summary = "The official Python library for the openai API" groups = ["default"] @@ -1656,14 +1656,14 @@ dependencies = [ "typing-extensions<5,>=4.7", ] files = [ - {file = "openai-1.12.0-py3-none-any.whl", hash = "sha256:a54002c814e05222e413664f651b5916714e4700d041d5cf5724d3ae1a3e3481"}, - {file = "openai-1.12.0.tar.gz", hash = "sha256:99c5d257d09ea6533d689d1cc77caa0ac679fa21efef8893d8b0832a86877f1b"}, + {file = "openai-1.13.3-py3-none-any.whl", hash = "sha256:5769b62abd02f350a8dd1a3a242d8972c947860654466171d60fb0972ae0a41c"}, + {file = "openai-1.13.3.tar.gz", hash = "sha256:ff6c6b3bc7327e715e4b3592a923a5a1c7519ff5dd764a83d69f633d49e77a7b"}, ] [[package]] name = "opentelemetry-api" -version = "1.22.0" -requires_python = ">=3.7" +version = "1.23.0" +requires_python = ">=3.8" summary = "OpenTelemetry Python API" groups = ["default"] dependencies = [ @@ -1671,50 +1671,48 @@ dependencies = [ "importlib-metadata<7.0,>=6.0", ] files = [ - {file = "opentelemetry_api-1.22.0-py3-none-any.whl", hash = "sha256:43621514301a7e9f5d06dd8013a1b450f30c2e9372b8e30aaeb4562abf2ce034"}, - {file = "opentelemetry_api-1.22.0.tar.gz", hash = "sha256:15ae4ca925ecf9cfdfb7a709250846fbb08072260fca08ade78056c502b86bed"}, + {file = "opentelemetry_api-1.23.0-py3-none-any.whl", hash = "sha256:cc03ea4025353048aadb9c64919099663664672ea1c6be6ddd8fee8e4cd5e774"}, + {file = "opentelemetry_api-1.23.0.tar.gz", hash = "sha256:14a766548c8dd2eb4dfc349739eb4c3893712a0daa996e5dbf945f9da665da9d"}, ] [[package]] name = "opentelemetry-exporter-otlp-proto-common" -version = "1.22.0" -requires_python = ">=3.7" +version = "1.23.0" +requires_python = ">=3.8" summary = "OpenTelemetry Protobuf encoding" groups = ["default"] dependencies = [ - "backoff<3.0.0,>=1.10.0; python_version >= \"3.7\"", - "opentelemetry-proto==1.22.0", + "opentelemetry-proto==1.23.0", ] files = [ - {file = "opentelemetry_exporter_otlp_proto_common-1.22.0-py3-none-any.whl", hash = "sha256:3f2538bec5312587f8676c332b3747f54c89fe6364803a807e217af4603201fa"}, - {file = "opentelemetry_exporter_otlp_proto_common-1.22.0.tar.gz", hash = "sha256:71ae2f81bc6d6fe408d06388826edc8933759b2ca3a97d24054507dc7cfce52d"}, + {file = "opentelemetry_exporter_otlp_proto_common-1.23.0-py3-none-any.whl", hash = "sha256:2a9e7e9d5a8b026b572684b6b24dcdefcaa58613d5ce3d644130b0c373c056c1"}, + {file = "opentelemetry_exporter_otlp_proto_common-1.23.0.tar.gz", hash = "sha256:35e4ea909e7a0b24235bd0aaf17fba49676527feb1823b46565ff246d5a1ab18"}, ] [[package]] name = "opentelemetry-exporter-otlp-proto-grpc" -version = "1.22.0" -requires_python = ">=3.7" +version = "1.23.0" +requires_python = ">=3.8" summary = "OpenTelemetry Collector Protobuf over gRPC Exporter" groups = ["default"] dependencies = [ - "backoff<3.0.0,>=1.10.0; python_version >= \"3.7\"", "deprecated>=1.2.6", "googleapis-common-protos~=1.52", "grpcio<2.0.0,>=1.0.0", "opentelemetry-api~=1.15", - "opentelemetry-exporter-otlp-proto-common==1.22.0", - "opentelemetry-proto==1.22.0", - "opentelemetry-sdk~=1.22.0", + "opentelemetry-exporter-otlp-proto-common==1.23.0", + "opentelemetry-proto==1.23.0", + "opentelemetry-sdk~=1.23.0", ] files = [ - {file = "opentelemetry_exporter_otlp_proto_grpc-1.22.0-py3-none-any.whl", hash = "sha256:b5bcadc129272004316a455e9081216d3380c1fc2231a928ea6a70aa90e173fb"}, - {file = "opentelemetry_exporter_otlp_proto_grpc-1.22.0.tar.gz", hash = "sha256:1e0e5aa4bbabc74942f06f268deffd94851d12a8dc30b02527472ef1729fe5b1"}, + {file = "opentelemetry_exporter_otlp_proto_grpc-1.23.0-py3-none-any.whl", hash = "sha256:40f9e3e7761eb34f2a1001f4543028783ac26e2db27e420d5374f2cca0182dad"}, + {file = "opentelemetry_exporter_otlp_proto_grpc-1.23.0.tar.gz", hash = "sha256:aa1a012eea5342bfef51fcf3f7f22601dcb0f0984a07ffe6025b2fbb6d91a2a9"}, ] [[package]] name = "opentelemetry-instrumentation" -version = "0.43b0" -requires_python = ">=3.7" +version = "0.44b0" +requires_python = ">=3.8" summary = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" groups = ["default"] dependencies = [ @@ -1723,135 +1721,135 @@ dependencies = [ "wrapt<2.0.0,>=1.0.0", ] files = [ - {file = "opentelemetry_instrumentation-0.43b0-py3-none-any.whl", hash = "sha256:0ff1334d7e359e27640e9d420024efeb73eacae464309c2e14ede7ba6c93967e"}, - {file = "opentelemetry_instrumentation-0.43b0.tar.gz", hash = "sha256:c3755da6c4be8033be0216d0501e11f4832690f4e2eca5a3576fbf113498f0f6"}, + {file = "opentelemetry_instrumentation-0.44b0-py3-none-any.whl", hash = "sha256:79560f386425176bcc60c59190064597096114c4a8e5154f1cb281bb4e47d2fc"}, + {file = "opentelemetry_instrumentation-0.44b0.tar.gz", hash = "sha256:8213d02d8c0987b9b26386ae3e091e0477d6331673123df736479322e1a50b48"}, ] [[package]] name = "opentelemetry-instrumentation-asgi" -version = "0.43b0" -requires_python = ">=3.7" +version = "0.44b0" +requires_python = ">=3.8" summary = "ASGI instrumentation for OpenTelemetry" groups = ["default"] dependencies = [ "asgiref~=3.0", "opentelemetry-api~=1.12", - "opentelemetry-instrumentation==0.43b0", - "opentelemetry-semantic-conventions==0.43b0", - "opentelemetry-util-http==0.43b0", + "opentelemetry-instrumentation==0.44b0", + "opentelemetry-semantic-conventions==0.44b0", + "opentelemetry-util-http==0.44b0", ] files = [ - {file = "opentelemetry_instrumentation_asgi-0.43b0-py3-none-any.whl", hash = "sha256:1f593829fa039e9367820736fb063e92acd15c25b53d7bcb5d319971b8e93fd7"}, - {file = "opentelemetry_instrumentation_asgi-0.43b0.tar.gz", hash = "sha256:3f6f19333dca31ef696672e4e36cb1c2613c71dc7e847c11ff36a37e1130dadc"}, + {file = "opentelemetry_instrumentation_asgi-0.44b0-py3-none-any.whl", hash = "sha256:0d95c84a8991008c8a8ac35e15d43cc7768a5bb46f95f129e802ad2990d7c366"}, + {file = "opentelemetry_instrumentation_asgi-0.44b0.tar.gz", hash = "sha256:72d4d28ec7ccd551eac11edc5ae8cac3586c0a228467d6a95fad7b6d4edd597a"}, ] [[package]] name = "opentelemetry-instrumentation-fastapi" -version = "0.43b0" -requires_python = ">=3.7" +version = "0.44b0" +requires_python = ">=3.8" summary = "OpenTelemetry FastAPI Instrumentation" groups = ["default"] dependencies = [ "opentelemetry-api~=1.12", - "opentelemetry-instrumentation-asgi==0.43b0", - "opentelemetry-instrumentation==0.43b0", - "opentelemetry-semantic-conventions==0.43b0", - "opentelemetry-util-http==0.43b0", + "opentelemetry-instrumentation-asgi==0.44b0", + "opentelemetry-instrumentation==0.44b0", + "opentelemetry-semantic-conventions==0.44b0", + "opentelemetry-util-http==0.44b0", ] files = [ - {file = "opentelemetry_instrumentation_fastapi-0.43b0-py3-none-any.whl", hash = "sha256:b79c044df68a52e07b35fa12a424e7cc0dd27ff0a171c5fdcc41dea9de8fc938"}, - {file = "opentelemetry_instrumentation_fastapi-0.43b0.tar.gz", hash = "sha256:2afaaf470622e1a2732182c68f6d2431ffe5e026a7edacd0f83605632b66347f"}, + {file = "opentelemetry_instrumentation_fastapi-0.44b0-py3-none-any.whl", hash = "sha256:4441482944bea6676816668d56deb94af990e8c6e9582c581047e5d84c91d3c9"}, + {file = "opentelemetry_instrumentation_fastapi-0.44b0.tar.gz", hash = "sha256:67ed10b93ad9d35238ae0be73cf8acbbb65a4a61fb7444d0aee5b0c492e294db"}, ] [[package]] name = "opentelemetry-proto" -version = "1.22.0" -requires_python = ">=3.7" +version = "1.23.0" +requires_python = ">=3.8" summary = "OpenTelemetry Python Proto" groups = ["default"] dependencies = [ "protobuf<5.0,>=3.19", ] files = [ - {file = "opentelemetry_proto-1.22.0-py3-none-any.whl", hash = "sha256:ce7188d22c75b6d0fe53e7fb58501613d0feade5139538e79dedd9420610fa0c"}, - {file = "opentelemetry_proto-1.22.0.tar.gz", hash = "sha256:9ec29169286029f17ca34ec1f3455802ffb90131642d2f545ece9a63e8f69003"}, + {file = "opentelemetry_proto-1.23.0-py3-none-any.whl", hash = "sha256:4c017deca052cb287a6003b7c989ed8b47af65baeb5d57ebf93dde0793f78509"}, + {file = "opentelemetry_proto-1.23.0.tar.gz", hash = "sha256:e6aaf8b7ace8d021942d546161401b83eed90f9f2cc6f13275008cea730e4651"}, ] [[package]] name = "opentelemetry-sdk" -version = "1.22.0" -requires_python = ">=3.7" +version = "1.23.0" +requires_python = ">=3.8" summary = "OpenTelemetry Python SDK" groups = ["default"] dependencies = [ - "opentelemetry-api==1.22.0", - "opentelemetry-semantic-conventions==0.43b0", + "opentelemetry-api==1.23.0", + "opentelemetry-semantic-conventions==0.44b0", "typing-extensions>=3.7.4", ] files = [ - {file = "opentelemetry_sdk-1.22.0-py3-none-any.whl", hash = "sha256:a730555713d7c8931657612a88a141e3a4fe6eb5523d9e2d5a8b1e673d76efa6"}, - {file = "opentelemetry_sdk-1.22.0.tar.gz", hash = "sha256:45267ac1f38a431fc2eb5d6e0c0d83afc0b78de57ac345488aa58c28c17991d0"}, + {file = "opentelemetry_sdk-1.23.0-py3-none-any.whl", hash = "sha256:a93c96990ac0f07c6d679e2f1015864ff7a4f5587122dd5af968034436efb1fd"}, + {file = "opentelemetry_sdk-1.23.0.tar.gz", hash = "sha256:9ddf60195837b59e72fd2033d6a47e2b59a0f74f0ec37d89387d89e3da8cab7f"}, ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.43b0" -requires_python = ">=3.7" +version = "0.44b0" +requires_python = ">=3.8" summary = "OpenTelemetry Semantic Conventions" groups = ["default"] files = [ - {file = "opentelemetry_semantic_conventions-0.43b0-py3-none-any.whl", hash = "sha256:291284d7c1bf15fdaddf309b3bd6d3b7ce12a253cec6d27144439819a15d8445"}, - {file = "opentelemetry_semantic_conventions-0.43b0.tar.gz", hash = "sha256:b9576fb890df479626fa624e88dde42d3d60b8b6c8ae1152ad157a8b97358635"}, + {file = "opentelemetry_semantic_conventions-0.44b0-py3-none-any.whl", hash = "sha256:7c434546c9cbd797ab980cc88bf9ff3f4a5a28f941117cad21694e43d5d92019"}, + {file = "opentelemetry_semantic_conventions-0.44b0.tar.gz", hash = "sha256:2e997cb28cd4ca81a25a9a43365f593d0c2b76be0685015349a89abdf1aa4ffa"}, ] [[package]] name = "opentelemetry-util-http" -version = "0.43b0" -requires_python = ">=3.7" +version = "0.44b0" +requires_python = ">=3.8" summary = "Web util for OpenTelemetry" groups = ["default"] files = [ - {file = "opentelemetry_util_http-0.43b0-py3-none-any.whl", hash = "sha256:f25a820784b030f6cb86b3d76e5676c769b75ed3f55a210bcdae0a5e175ebadb"}, - {file = "opentelemetry_util_http-0.43b0.tar.gz", hash = "sha256:3ff6ab361dbe99fc81200d625603c0fb890c055c6e416a3e6d661ddf47a6c7f7"}, + {file = "opentelemetry_util_http-0.44b0-py3-none-any.whl", hash = "sha256:ff018ab6a2fa349537ff21adcef99a294248b599be53843c44f367aef6bccea5"}, + {file = "opentelemetry_util_http-0.44b0.tar.gz", hash = "sha256:75896dffcbbeb5df5429ad4526e22307fc041a27114e0c5bfd90bb219381e68f"}, ] [[package]] name = "orjson" -version = "3.9.14" +version = "3.9.15" requires_python = ">=3.8" summary = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" groups = ["default"] files = [ - {file = "orjson-3.9.14-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:793f6c9448ab6eb7d4974b4dde3f230345c08ca6c7995330fbceeb43a5c8aa5e"}, - {file = "orjson-3.9.14-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6bc7928d161840096adc956703494b5c0193ede887346f028216cac0af87500"}, - {file = "orjson-3.9.14-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:58b36f54da759602d8e2f7dad958752d453dfe2c7122767bc7f765e17dc59959"}, - {file = "orjson-3.9.14-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:abcda41ecdc950399c05eff761c3de91485d9a70d8227cb599ad3a66afe93bcc"}, - {file = "orjson-3.9.14-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df76ecd17b1b3627bddfd689faaf206380a1a38cc9f6c4075bd884eaedcf46c2"}, - {file = "orjson-3.9.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d450a8e0656efb5d0fcb062157b918ab02dcca73278975b4ee9ea49e2fcf5bd5"}, - {file = "orjson-3.9.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:95c03137b0cf66517c8baa65770507a756d3a89489d8ecf864ea92348e1beabe"}, - {file = "orjson-3.9.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20837e10835c98973673406d6798e10f821e7744520633811a5a3d809762d8cc"}, - {file = "orjson-3.9.14-cp310-none-win32.whl", hash = "sha256:1f7b6f3ef10ae8e3558abb729873d033dbb5843507c66b1c0767e32502ba96bb"}, - {file = "orjson-3.9.14-cp310-none-win_amd64.whl", hash = "sha256:ea890e6dc1711aeec0a33b8520e395c2f3d59ead5b4351a788e06bf95fc7ba81"}, - {file = "orjson-3.9.14-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c19009ff37f033c70acd04b636380379499dac2cba27ae7dfc24f304deabbc81"}, - {file = "orjson-3.9.14-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19cdea0664aec0b7f385be84986d4defd3334e9c3c799407686ee1c26f7b8251"}, - {file = "orjson-3.9.14-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:135d518f73787ce323b1a5e21fb854fe22258d7a8ae562b81a49d6c7f826f2a3"}, - {file = "orjson-3.9.14-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d2cf1d0557c61c75e18cf7d69fb689b77896e95553e212c0cc64cf2087944b84"}, - {file = "orjson-3.9.14-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7c11667421df2d8b18b021223505dcc3ee51be518d54e4dc49161ac88ac2b87"}, - {file = "orjson-3.9.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2eefc41ba42e75ed88bc396d8fe997beb20477f3e7efa000cd7a47eda452fbb2"}, - {file = "orjson-3.9.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:917311d6a64d1c327c0dfda1e41f3966a7fb72b11ca7aa2e7a68fcccc7db35d9"}, - {file = "orjson-3.9.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4dc1c132259b38d12c6587d190cd09cd76e3b5273ce71fe1372437b4cbc65f6f"}, - {file = "orjson-3.9.14-cp311-none-win32.whl", hash = "sha256:6f39a10408478f4c05736a74da63727a1ae0e83e3533d07b19443400fe8591ca"}, - {file = "orjson-3.9.14-cp311-none-win_amd64.whl", hash = "sha256:26280a7fcb62d8257f634c16acebc3bec626454f9ab13558bbf7883b9140760e"}, - {file = "orjson-3.9.14-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:08e722a8d06b13b67a51f247a24938d1a94b4b3862e40e0eef3b2e98c99cd04c"}, - {file = "orjson-3.9.14-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2591faa0c031cf3f57e5bce1461cfbd6160f3f66b5a72609a130924917cb07d"}, - {file = "orjson-3.9.14-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2450d87dd7b4f277f4c5598faa8b49a0c197b91186c47a2c0b88e15531e4e3e"}, - {file = "orjson-3.9.14-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:90903d2908158a2c9077a06f11e27545de610af690fb178fd3ba6b32492d4d1c"}, - {file = "orjson-3.9.14-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce6f095eef0026eae76fc212f20f786011ecf482fc7df2f4c272a8ae6dd7b1ef"}, - {file = "orjson-3.9.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:751250a31fef2bac05a2da2449aae7142075ea26139271f169af60456d8ad27a"}, - {file = "orjson-3.9.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9a1af21160a38ee8be3f4fcf24ee4b99e6184cadc7f915d599f073f478a94d2c"}, - {file = "orjson-3.9.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:449bf090b2aa4e019371d7511a6ea8a5a248139205c27d1834bb4b1e3c44d936"}, - {file = "orjson-3.9.14-cp312-none-win_amd64.whl", hash = "sha256:a603161318ff699784943e71f53899983b7dee571b4dd07c336437c9c5a272b0"}, - {file = "orjson-3.9.14.tar.gz", hash = "sha256:06fb40f8e49088ecaa02f1162581d39e2cf3fd9dbbfe411eb2284147c99bad79"}, + {file = "orjson-3.9.15-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d61f7ce4727a9fa7680cd6f3986b0e2c732639f46a5e0156e550e35258aa313a"}, + {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4feeb41882e8aa17634b589533baafdceb387e01e117b1ec65534ec724023d04"}, + {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fbbeb3c9b2edb5fd044b2a070f127a0ac456ffd079cb82746fc84af01ef021a4"}, + {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b66bcc5670e8a6b78f0313bcb74774c8291f6f8aeef10fe70e910b8040f3ab75"}, + {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2973474811db7b35c30248d1129c64fd2bdf40d57d84beed2a9a379a6f57d0ab"}, + {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fe41b6f72f52d3da4db524c8653e46243c8c92df826ab5ffaece2dba9cccd58"}, + {file = "orjson-3.9.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4228aace81781cc9d05a3ec3a6d2673a1ad0d8725b4e915f1089803e9efd2b99"}, + {file = "orjson-3.9.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f7b65bfaf69493c73423ce9db66cfe9138b2f9ef62897486417a8fcb0a92bfe"}, + {file = "orjson-3.9.15-cp310-none-win32.whl", hash = "sha256:2d99e3c4c13a7b0fb3792cc04c2829c9db07838fb6973e578b85c1745e7d0ce7"}, + {file = "orjson-3.9.15-cp310-none-win_amd64.whl", hash = "sha256:b725da33e6e58e4a5d27958568484aa766e825e93aa20c26c91168be58e08cbb"}, + {file = "orjson-3.9.15-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c8e8fe01e435005d4421f183038fc70ca85d2c1e490f51fb972db92af6e047c2"}, + {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87f1097acb569dde17f246faa268759a71a2cb8c96dd392cd25c668b104cad2f"}, + {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff0f9913d82e1d1fadbd976424c316fbc4d9c525c81d047bbdd16bd27dd98cfc"}, + {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8055ec598605b0077e29652ccfe9372247474375e0e3f5775c91d9434e12d6b1"}, + {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6768a327ea1ba44c9114dba5fdda4a214bdb70129065cd0807eb5f010bfcbb5"}, + {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12365576039b1a5a47df01aadb353b68223da413e2e7f98c02403061aad34bde"}, + {file = "orjson-3.9.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:71c6b009d431b3839d7c14c3af86788b3cfac41e969e3e1c22f8a6ea13139404"}, + {file = "orjson-3.9.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e18668f1bd39e69b7fed19fa7cd1cd110a121ec25439328b5c89934e6d30d357"}, + {file = "orjson-3.9.15-cp311-none-win32.whl", hash = "sha256:62482873e0289cf7313461009bf62ac8b2e54bc6f00c6fabcde785709231a5d7"}, + {file = "orjson-3.9.15-cp311-none-win_amd64.whl", hash = "sha256:b3d336ed75d17c7b1af233a6561cf421dee41d9204aa3cfcc6c9c65cd5bb69a8"}, + {file = "orjson-3.9.15-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:82425dd5c7bd3adfe4e94c78e27e2fa02971750c2b7ffba648b0f5d5cc016a73"}, + {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c51378d4a8255b2e7c1e5cc430644f0939539deddfa77f6fac7b56a9784160a"}, + {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6ae4e06be04dc00618247c4ae3f7c3e561d5bc19ab6941427f6d3722a0875ef7"}, + {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcef128f970bb63ecf9a65f7beafd9b55e3aaf0efc271a4154050fc15cdb386e"}, + {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b72758f3ffc36ca566ba98a8e7f4f373b6c17c646ff8ad9b21ad10c29186f00d"}, + {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c57bc7b946cf2efa67ac55766e41764b66d40cbd9489041e637c1304400494"}, + {file = "orjson-3.9.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:946c3a1ef25338e78107fba746f299f926db408d34553b4754e90a7de1d44068"}, + {file = "orjson-3.9.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2f256d03957075fcb5923410058982aea85455d035607486ccb847f095442bda"}, + {file = "orjson-3.9.15-cp312-none-win_amd64.whl", hash = "sha256:5bb399e1b49db120653a31463b4a7b27cf2fbfe60469546baf681d1b39f4edf2"}, + {file = "orjson-3.9.15.tar.gz", hash = "sha256:95cae920959d772f30ab36d3b25f83bb0f3be671e986c72ce22f8fa700dae061"}, ] [[package]] @@ -1878,7 +1876,7 @@ files = [ [[package]] name = "pandas" -version = "2.2.0" +version = "2.2.1" requires_python = ">=3.9" summary = "Powerful data structures for data analysis, time series, and statistics" groups = ["default"] @@ -1891,28 +1889,28 @@ dependencies = [ "tzdata>=2022.7", ] files = [ - {file = "pandas-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8108ee1712bb4fa2c16981fba7e68b3f6ea330277f5ca34fa8d557e986a11670"}, - {file = "pandas-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:736da9ad4033aeab51d067fc3bd69a0ba36f5a60f66a527b3d72e2030e63280a"}, - {file = "pandas-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38e0b4fc3ddceb56ec8a287313bc22abe17ab0eb184069f08fc6a9352a769b18"}, - {file = "pandas-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20404d2adefe92aed3b38da41d0847a143a09be982a31b85bc7dd565bdba0f4e"}, - {file = "pandas-2.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7ea3ee3f125032bfcade3a4cf85131ed064b4f8dd23e5ce6fa16473e48ebcaf5"}, - {file = "pandas-2.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f9670b3ac00a387620489dfc1bca66db47a787f4e55911f1293063a78b108df1"}, - {file = "pandas-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a946f210383c7e6d16312d30b238fd508d80d927014f3b33fb5b15c2f895430"}, - {file = "pandas-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a1b438fa26b208005c997e78672f1aa8138f67002e833312e6230f3e57fa87d5"}, - {file = "pandas-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8ce2fbc8d9bf303ce54a476116165220a1fedf15985b09656b4b4275300e920b"}, - {file = "pandas-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2707514a7bec41a4ab81f2ccce8b382961a29fbe9492eab1305bb075b2b1ff4f"}, - {file = "pandas-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85793cbdc2d5bc32620dc8ffa715423f0c680dacacf55056ba13454a5be5de88"}, - {file = "pandas-2.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cfd6c2491dc821b10c716ad6776e7ab311f7df5d16038d0b7458bc0b67dc10f3"}, - {file = "pandas-2.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a146b9dcacc3123aa2b399df1a284de5f46287a4ab4fbfc237eac98a92ebcb71"}, - {file = "pandas-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbc1b53c0e1fdf16388c33c3cca160f798d38aea2978004dd3f4d3dec56454c9"}, - {file = "pandas-2.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a41d06f308a024981dcaa6c41f2f2be46a6b186b902c94c2674e8cb5c42985bc"}, - {file = "pandas-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:159205c99d7a5ce89ecfc37cb08ed179de7783737cea403b295b5eda8e9c56d1"}, - {file = "pandas-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1e1f3861ea9132b32f2133788f3b14911b68102d562715d71bd0013bc45440"}, - {file = "pandas-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:761cb99b42a69005dec2b08854fb1d4888fdf7b05db23a8c5a099e4b886a2106"}, - {file = "pandas-2.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a20628faaf444da122b2a64b1e5360cde100ee6283ae8effa0d8745153809a2e"}, - {file = "pandas-2.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f5be5d03ea2073627e7111f61b9f1f0d9625dc3c4d8dda72cc827b0c58a1d042"}, - {file = "pandas-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:a626795722d893ed6aacb64d2401d017ddc8a2341b49e0384ab9bf7112bdec30"}, - {file = "pandas-2.2.0.tar.gz", hash = "sha256:30b83f7c3eb217fb4d1b494a57a2fda5444f17834f5df2de6b2ffff68dc3c8e2"}, + {file = "pandas-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8df8612be9cd1c7797c93e1c5df861b2ddda0b48b08f2c3eaa0702cf88fb5f88"}, + {file = "pandas-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0f573ab277252ed9aaf38240f3b54cfc90fff8e5cab70411ee1d03f5d51f3944"}, + {file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f02a3a6c83df4026e55b63c1f06476c9aa3ed6af3d89b4f04ea656ccdaaaa359"}, + {file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c38ce92cb22a4bea4e3929429aa1067a454dcc9c335799af93ba9be21b6beb51"}, + {file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c2ce852e1cf2509a69e98358e8458775f89599566ac3775e70419b98615f4b06"}, + {file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53680dc9b2519cbf609c62db3ed7c0b499077c7fefda564e330286e619ff0dd9"}, + {file = "pandas-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:94e714a1cca63e4f5939cdce5f29ba8d415d85166be3441165edd427dc9f6bc0"}, + {file = "pandas-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f821213d48f4ab353d20ebc24e4faf94ba40d76680642fb7ce2ea31a3ad94f9b"}, + {file = "pandas-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c70e00c2d894cb230e5c15e4b1e1e6b2b478e09cf27cc593a11ef955b9ecc81a"}, + {file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e97fbb5387c69209f134893abc788a6486dbf2f9e511070ca05eed4b930b1b02"}, + {file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101d0eb9c5361aa0146f500773395a03839a5e6ecde4d4b6ced88b7e5a1a6403"}, + {file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7d2ed41c319c9fb4fd454fe25372028dfa417aacb9790f68171b2e3f06eae8cd"}, + {file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:af5d3c00557d657c8773ef9ee702c61dd13b9d7426794c9dfeb1dc4a0bf0ebc7"}, + {file = "pandas-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:06cf591dbaefb6da9de8472535b185cba556d0ce2e6ed28e21d919704fef1a9e"}, + {file = "pandas-2.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:88ecb5c01bb9ca927ebc4098136038519aa5d66b44671861ffab754cae75102c"}, + {file = "pandas-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:04f6ec3baec203c13e3f8b139fb0f9f86cd8c0b94603ae3ae8ce9a422e9f5bee"}, + {file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a935a90a76c44fe170d01e90a3594beef9e9a6220021acfb26053d01426f7dc2"}, + {file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c391f594aae2fd9f679d419e9a4d5ba4bce5bb13f6a989195656e7dc4b95c8f0"}, + {file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9d1265545f579edf3f8f0cb6f89f234f5e44ba725a34d86535b1a1d38decbccc"}, + {file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11940e9e3056576ac3244baef2fedade891977bcc1cb7e5cc8f8cc7d603edc89"}, + {file = "pandas-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:4acf681325ee1c7f950d058b05a820441075b0dd9a2adf5c4835b9bc056bf4fb"}, + {file = "pandas-2.2.1.tar.gz", hash = "sha256:0ab90f87093c13f3e8fa45b48ba9f39181046e8f3317d3aadb2fffbb1b978572"}, ] [[package]] @@ -2096,23 +2094,23 @@ files = [ [[package]] name = "pydantic" -version = "2.6.1" +version = "2.6.3" requires_python = ">=3.8" summary = "Data validation using Python type hints" groups = ["default"] dependencies = [ "annotated-types>=0.4.0", - "pydantic-core==2.16.2", + "pydantic-core==2.16.3", "typing-extensions>=4.6.1", ] files = [ - {file = "pydantic-2.6.1-py3-none-any.whl", hash = "sha256:0b6a909df3192245cb736509a92ff69e4fef76116feffec68e93a567347bae6f"}, - {file = "pydantic-2.6.1.tar.gz", hash = "sha256:4fd5c182a2488dc63e6d32737ff19937888001e2a6d86e94b3f233104a5d1fa9"}, + {file = "pydantic-2.6.3-py3-none-any.whl", hash = "sha256:72c6034df47f46ccdf81869fddb81aade68056003900a8724a4f160700016a2a"}, + {file = "pydantic-2.6.3.tar.gz", hash = "sha256:e07805c4c7f5c6826e33a1d4c9d47950d7eaf34868e2690f8594d2e30241f11f"}, ] [[package]] name = "pydantic-core" -version = "2.16.2" +version = "2.16.3" requires_python = ">=3.8" summary = "" groups = ["default"] @@ -2120,61 +2118,61 @@ dependencies = [ "typing-extensions!=4.7.0,>=4.6.0", ] files = [ - {file = "pydantic_core-2.16.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3fab4e75b8c525a4776e7630b9ee48aea50107fea6ca9f593c98da3f4d11bf7c"}, - {file = "pydantic_core-2.16.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8bde5b48c65b8e807409e6f20baee5d2cd880e0fad00b1a811ebc43e39a00ab2"}, - {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2924b89b16420712e9bb8192396026a8fbd6d8726224f918353ac19c4c043d2a"}, - {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16aa02e7a0f539098e215fc193c8926c897175d64c7926d00a36188917717a05"}, - {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:936a787f83db1f2115ee829dd615c4f684ee48ac4de5779ab4300994d8af325b"}, - {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:459d6be6134ce3b38e0ef76f8a672924460c455d45f1ad8fdade36796df1ddc8"}, - {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9ee4febb249c591d07b2d4dd36ebcad0ccd128962aaa1801508320896575ef"}, - {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40a0bd0bed96dae5712dab2aba7d334a6c67cbcac2ddfca7dbcc4a8176445990"}, - {file = "pydantic_core-2.16.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:870dbfa94de9b8866b37b867a2cb37a60c401d9deb4a9ea392abf11a1f98037b"}, - {file = "pydantic_core-2.16.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:308974fdf98046db28440eb3377abba274808bf66262e042c412eb2adf852731"}, - {file = "pydantic_core-2.16.2-cp310-none-win32.whl", hash = "sha256:a477932664d9611d7a0816cc3c0eb1f8856f8a42435488280dfbf4395e141485"}, - {file = "pydantic_core-2.16.2-cp310-none-win_amd64.whl", hash = "sha256:8f9142a6ed83d90c94a3efd7af8873bf7cefed2d3d44387bf848888482e2d25f"}, - {file = "pydantic_core-2.16.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:406fac1d09edc613020ce9cf3f2ccf1a1b2f57ab00552b4c18e3d5276c67eb11"}, - {file = "pydantic_core-2.16.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce232a6170dd6532096cadbf6185271e4e8c70fc9217ebe105923ac105da9978"}, - {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a90fec23b4b05a09ad988e7a4f4e081711a90eb2a55b9c984d8b74597599180f"}, - {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8aafeedb6597a163a9c9727d8a8bd363a93277701b7bfd2749fbefee2396469e"}, - {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9957433c3a1b67bdd4c63717eaf174ebb749510d5ea612cd4e83f2d9142f3fc8"}, - {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0d7a9165167269758145756db43a133608a531b1e5bb6a626b9ee24bc38a8f7"}, - {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dffaf740fe2e147fedcb6b561353a16243e654f7fe8e701b1b9db148242e1272"}, - {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ed79883b4328b7f0bd142733d99c8e6b22703e908ec63d930b06be3a0e7113"}, - {file = "pydantic_core-2.16.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cf903310a34e14651c9de056fcc12ce090560864d5a2bb0174b971685684e1d8"}, - {file = "pydantic_core-2.16.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:46b0d5520dbcafea9a8645a8164658777686c5c524d381d983317d29687cce97"}, - {file = "pydantic_core-2.16.2-cp311-none-win32.whl", hash = "sha256:70651ff6e663428cea902dac297066d5c6e5423fda345a4ca62430575364d62b"}, - {file = "pydantic_core-2.16.2-cp311-none-win_amd64.whl", hash = "sha256:98dc6f4f2095fc7ad277782a7c2c88296badcad92316b5a6e530930b1d475ebc"}, - {file = "pydantic_core-2.16.2-cp311-none-win_arm64.whl", hash = "sha256:ef6113cd31411eaf9b39fc5a8848e71c72656fd418882488598758b2c8c6dfa0"}, - {file = "pydantic_core-2.16.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:88646cae28eb1dd5cd1e09605680c2b043b64d7481cdad7f5003ebef401a3039"}, - {file = "pydantic_core-2.16.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7b883af50eaa6bb3299780651e5be921e88050ccf00e3e583b1e92020333304b"}, - {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bf26c2e2ea59d32807081ad51968133af3025c4ba5753e6a794683d2c91bf6e"}, - {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99af961d72ac731aae2a1b55ccbdae0733d816f8bfb97b41909e143de735f522"}, - {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02906e7306cb8c5901a1feb61f9ab5e5c690dbbeaa04d84c1b9ae2a01ebe9379"}, - {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5362d099c244a2d2f9659fb3c9db7c735f0004765bbe06b99be69fbd87c3f15"}, - {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ac426704840877a285d03a445e162eb258924f014e2f074e209d9b4ff7bf380"}, - {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b94cbda27267423411c928208e89adddf2ea5dd5f74b9528513f0358bba019cb"}, - {file = "pydantic_core-2.16.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6db58c22ac6c81aeac33912fb1af0e930bc9774166cdd56eade913d5f2fff35e"}, - {file = "pydantic_core-2.16.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396fdf88b1b503c9c59c84a08b6833ec0c3b5ad1a83230252a9e17b7dfb4cffc"}, - {file = "pydantic_core-2.16.2-cp312-none-win32.whl", hash = "sha256:7c31669e0c8cc68400ef0c730c3a1e11317ba76b892deeefaf52dcb41d56ed5d"}, - {file = "pydantic_core-2.16.2-cp312-none-win_amd64.whl", hash = "sha256:a3b7352b48fbc8b446b75f3069124e87f599d25afb8baa96a550256c031bb890"}, - {file = "pydantic_core-2.16.2-cp312-none-win_arm64.whl", hash = "sha256:a9e523474998fb33f7c1a4d55f5504c908d57add624599e095c20fa575b8d943"}, - {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f60f920691a620b03082692c378661947d09415743e437a7478c309eb0e4f82"}, - {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:47924039e785a04d4a4fa49455e51b4eb3422d6eaacfde9fc9abf8fdef164e8a"}, - {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6294e76b0380bb7a61eb8a39273c40b20beb35e8c87ee101062834ced19c545"}, - {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe56851c3f1d6f5384b3051c536cc81b3a93a73faf931f404fef95217cf1e10d"}, - {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9d776d30cde7e541b8180103c3f294ef7c1862fd45d81738d156d00551005784"}, - {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:72f7919af5de5ecfaf1eba47bf9a5d8aa089a3340277276e5636d16ee97614d7"}, - {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:4bfcbde6e06c56b30668a0c872d75a7ef3025dc3c1823a13cf29a0e9b33f67e8"}, - {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ff7c97eb7a29aba230389a2661edf2e9e06ce616c7e35aa764879b6894a44b25"}, - {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9b5f13857da99325dcabe1cc4e9e6a3d7b2e2c726248ba5dd4be3e8e4a0b6d0e"}, - {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a7e41e3ada4cca5f22b478c08e973c930e5e6c7ba3588fb8e35f2398cdcc1545"}, - {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60eb8ceaa40a41540b9acae6ae7c1f0a67d233c40dc4359c256ad2ad85bdf5e5"}, - {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7beec26729d496a12fd23cf8da9944ee338c8b8a17035a560b585c36fe81af20"}, - {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:22c5f022799f3cd6741e24f0443ead92ef42be93ffda0d29b2597208c94c3753"}, - {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:eca58e319f4fd6df004762419612122b2c7e7d95ffafc37e890252f869f3fb2a"}, - {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed957db4c33bc99895f3a1672eca7e80e8cda8bd1e29a80536b4ec2153fa9804"}, - {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:459c0d338cc55d099798618f714b21b7ece17eb1a87879f2da20a3ff4c7628e2"}, - {file = "pydantic_core-2.16.2.tar.gz", hash = "sha256:0ba503850d8b8dcc18391f10de896ae51d37fe5fe43dbfb6a35c5c5cad271a06"}, + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"}, + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"}, + {file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"}, + {file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"}, + {file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"}, + {file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"}, + {file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"}, + {file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"}, + {file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"}, + {file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"}, + {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"}, ] [[package]] @@ -2608,13 +2606,13 @@ files = [ [[package]] name = "setuptools" -version = "69.1.0" +version = "69.1.1" requires_python = ">=3.8" summary = "Easily download, build, install, upgrade, and uninstall Python packages" groups = ["default"] files = [ - {file = "setuptools-69.1.0-py3-none-any.whl", hash = "sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6"}, - {file = "setuptools-69.1.0.tar.gz", hash = "sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401"}, + {file = "setuptools-69.1.1-py3-none-any.whl", hash = "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56"}, + {file = "setuptools-69.1.1.tar.gz", hash = "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8"}, ] [[package]] @@ -2652,13 +2650,13 @@ files = [ [[package]] name = "sniffio" -version = "1.3.0" +version = "1.3.1" requires_python = ">=3.7" summary = "Sniff out which async library your code is running under" groups = ["default"] files = [ - {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, - {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] [[package]] @@ -2983,13 +2981,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.9.0" +version = "4.10.0" requires_python = ">=3.8" summary = "Backported and Experimental Type Hints for Python 3.8+" groups = ["default"] files = [ - {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, - {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, ] [[package]] diff --git a/repo_agent/api_client.py b/repo_agent/api_client.py deleted file mode 100644 index 79c6608..0000000 --- a/repo_agent/api_client.py +++ /dev/null @@ -1,43 +0,0 @@ -import requests -from requests.exceptions import RequestException -from time import sleep -from repo_agent.exceptions import APIError -""" -OpenAIAPIClient 类将负责与OpenAI API的所有通信,包括发送请求和处理响应。它还将处理API调用的重试逻辑。 -""" -class OpenAIAPIClient: - def __init__(self, api_key): - self.api_key = api_key - self.headers = { - 'Authorization': f'Bearer {self.api_key}', - 'Content-Type': 'application/json' - } - - def call_api(self, endpoint, payload, retries=3, backoff_factor=0.3): - """ - Call the OpenAI API with the given endpoint and payload. - Retries on failures with exponential backoff. - - Parameters: - endpoint (str): The API endpoint to call. - payload (dict): The payload to send in the API call. - retries (int): Number of times to retry on failure. - backoff_factor (float): Factor by which to increase delay between retries. - - Returns: - dict: The JSON response from the API. - - Raises: - APIError: An error occurred when calling the API. - """ - for attempt in range(retries): - try: - response = requests.post(endpoint, json=payload, headers=self.headers) - response.raise_for_status() - return response.json() - except RequestException as e: - if attempt < retries - 1: - sleep((2 ** attempt) * backoff_factor) - continue - else: - raise APIError(e, status_code=response.status_code if response else None) diff --git a/repo_agent/chat_engine.py b/repo_agent/chat_engine.py index c4f150b..985025a 100644 --- a/repo_agent/chat_engine.py +++ b/repo_agent/chat_engine.py @@ -1,20 +1,16 @@ -import os, json +import json import sys -from openai import OpenAI -from openai import APIConnectionError +from attr import dataclass import tiktoken import time import inspect -from collections import defaultdict -from colorama import Fore, Style - from repo_agent.log import logger from repo_agent.config import language_mapping, max_input_tokens_map from repo_agent.prompt import SYS_PROMPT, USR_PROMPT from repo_agent.doc_meta_info import DocItem -import inspect from repo_agent.log import logger -from repo_agent.api_client import OpenAIAPIClient +from openai import OpenAI, APIConnectionError + def get_import_statements(): source_lines = inspect.getsourcelines(sys.modules[__name__])[0] @@ -25,9 +21,9 @@ def get_import_statements(): ] return import_lines +@dataclass class ResponseMessage: - def __init__(self, content): - self.content = content + content: str class ChatEngine: @@ -37,22 +33,22 @@ class ChatEngine: def __init__(self, CONFIG, project_manager): self.config = CONFIG - self.project_manager = project_manager - # self.api_client = OpenAIAPIClient(CONFIG['api_key']) + self.project_manager = project_manager def num_tokens_from_string(self, string: str, encoding_name="cl100k_base") -> int: """Returns the number of tokens in a text string.""" encoding = tiktoken.get_encoding(encoding_name) num_tokens = len(encoding.encode(string)) return num_tokens - def reduce_input_length(self, shorten_attempt, prompt_data): """ Reduces the length of the input prompts by modifying the sys_prompt contents. """ - logger.info(f"Attempt {shorten_attempt + 1} / 2 to reduce the length of the messages.") + logger.info( + f"Attempt {shorten_attempt + 1} / 2 to reduce the length of the messages." + ) if shorten_attempt == 0: # First attempt, remove project_structure and project_structure_prefix prompt_data.project_structure = "" @@ -71,27 +67,8 @@ def reduce_input_length(self, shorten_attempt, prompt_data): sys_prompt = SYS_PROMPT.format(**prompt_data) return sys_prompt - def generate_response(self, model, sys_prompt, usr_prompt, max_tokens): - """ - Generates a response from the OpenAI API based on the provided prompts and model. - If the response is not successful, it raises an OpenAIResponseError. - - Parameters: - model (str): The model to be used for the API request. - sys_prompt (str): The system prompt. - usr_prompt (str): The user prompt. - max_tokens (int): The maximum number of tokens allowed in the output. - - Returns: - str: The response message from the API. - - Raises: - OpenAIError: If an error occurs during the API call. - OpenAIResponseError: If the response message is None or not as expected. - """ - # Get basic configuration client = OpenAI( api_key=self.config["api_keys"][model][0]["api_key"], base_url=self.config["api_keys"][model][0]["base_url"], @@ -114,12 +91,15 @@ def generate_response(self, model, sys_prompt, usr_prompt, max_tokens): return response_message - - def attempt_generate_response(self, model, sys_prompt, usr_prompt, max_tokens, max_attempts=5): + def attempt_generate_response( + self, model, sys_prompt, usr_prompt, max_tokens, max_attempts=5 + ): attempt = 0 while attempt < max_attempts: try: - response_message = self.generate_response(model, sys_prompt, usr_prompt, max_tokens) + response_message = self.generate_response( + model, sys_prompt, usr_prompt, max_tokens + ) # 如果 response_message 是 None,则继续下一次循环 if response_message is None: @@ -128,7 +108,9 @@ def attempt_generate_response(self, model, sys_prompt, usr_prompt, max_tokens, m return response_message except APIConnectionError as e: - logger.error(f"Connection error: {e}. Attempt {attempt + 1} of {max_attempts}") + logger.error( + f"Connection error: {e}. Attempt {attempt + 1} of {max_attempts}" + ) # Retry after 7 seconds time.sleep(7) attempt += 1 @@ -145,182 +127,187 @@ def attempt_generate_response(self, model, sys_prompt, usr_prompt, max_tokens, m time.sleep(10) attempt += 1 if attempt == max_attempts: - response_message = ResponseMessage("An unknown error occurred while generating this documentation after many tries.") + response_message = ResponseMessage( + "An unknown error occurred while generating this documentation after many tries." + ) return response_message - - -def generate_doc(self, doc_item: DocItem, file_handler): - code_info = doc_item.content - referenced = len(doc_item.who_reference_me) > 0 - - code_type = code_info["type"] - code_name = code_info["name"] - code_content = code_info["code_content"] - have_return = code_info["have_return"] - who_reference_me = doc_item.who_reference_me_name_list - reference_who = doc_item.reference_who_name_list - file_path = doc_item.get_full_name() - doc_item_path = file_path + "/" + code_name - - # 树结构路径通过全局信息中的who reference me 和 reference who + 自身的file_path来获取 - # 使用 ProjectManager 实例来获取项目结构 - project_structure = self.project_manager.build_path_tree( - who_reference_me, reference_who, doc_item_path - ) - - # project_manager = ProjectManager(repo_path=file_handler.repo_path, project_hierarchy=file_handler.project_hierarchy) - # project_structure = project_manager.get_project_structure() - # file_path = os.path.join(file_handler.repo_path, file_handler.file_path) - # code_from_referencer = get_code_from_json(project_manager.project_hierarchy, referencer) # - # referenced = True if len(code_from_referencer) > 0 else False - # referencer_content = '\n'.join([f'File_Path:{file_path}\n' + '\n'.join([f'Corresponding code as follows:\n{code}\n[End of this part of code]' for code in codes]) + f'\n[End of {file_path}]' for file_path, codes in code_from_referencer.items()]) - - def get_referenced_prompt(doc_item: DocItem) -> str: - if len(doc_item.reference_who) == 0: - return "" - prompt = [ - """As you can see, the code calls the following objects, their code and docs are as following:""" - ] - for k, reference_item in enumerate(doc_item.reference_who): - instance_prompt = ( - f"""obj: {reference_item.get_full_name()}\nDocument: \n{reference_item.md_content[-1] if len(reference_item.md_content) > 0 else 'None'}\nRaw code:```\n{reference_item.content['code_content'] if 'code_content' in reference_item.content.keys() else ''}\n```""" - + "=" * 10 - ) - prompt.append(instance_prompt) - return "\n".join(prompt) - - def get_referencer_prompt(doc_item: DocItem) -> str: - if len(doc_item.who_reference_me) == 0: - return "" - prompt = [ - """Also, the code has been called by the following objects, their code and docs are as following:""" - ] - for k, referencer_item in enumerate(doc_item.who_reference_me): - instance_prompt = ( - f"""obj: {referencer_item.get_full_name()}\nDocument: \n{referencer_item.md_content[-1] if len(referencer_item.md_content) > 0 else 'None'}\nRaw code:```\n{referencer_item.content['code_content'] if 'code_content' in referencer_item.content.keys() else 'None'}\n```""" - + "=" * 10 + def generate_doc(self, doc_item: DocItem, file_handler): + code_info = doc_item.content + referenced = len(doc_item.who_reference_me) > 0 + + code_type = code_info["type"] + code_name = code_info["name"] + code_content = code_info["code_content"] + have_return = code_info["have_return"] + who_reference_me = doc_item.who_reference_me_name_list + reference_who = doc_item.reference_who_name_list + file_path = doc_item.get_full_name() + doc_item_path = file_path + "/" + code_name + + # 树结构路径通过全局信息中的who reference me 和 reference who + 自身的file_path来获取 + # 使用 ProjectManager 实例来获取项目结构 + project_structure = self.project_manager.build_path_tree( + who_reference_me, reference_who, doc_item_path + ) + + # project_manager = ProjectManager(repo_path=file_handler.repo_path, project_hierarchy=file_handler.project_hierarchy) + # project_structure = project_manager.get_project_structure() + # file_path = os.path.join(file_handler.repo_path, file_handler.file_path) + # code_from_referencer = get_code_from_json(project_manager.project_hierarchy, referencer) # + # referenced = True if len(code_from_referencer) > 0 else False + # referencer_content = '\n'.join([f'File_Path:{file_path}\n' + '\n'.join([f'Corresponding code as follows:\n{code}\n[End of this part of code]' for code in codes]) + f'\n[End of {file_path}]' for file_path, codes in code_from_referencer.items()]) + + def get_referenced_prompt(doc_item: DocItem) -> str: + if len(doc_item.reference_who) == 0: + return "" + prompt = [ + """As you can see, the code calls the following objects, their code and docs are as following:""" + ] + for k, reference_item in enumerate(doc_item.reference_who): + instance_prompt = ( + f"""obj: {reference_item.get_full_name()}\nDocument: \n{reference_item.md_content[-1] if len(reference_item.md_content) > 0 else 'None'}\nRaw code:```\n{reference_item.content['code_content'] if 'code_content' in reference_item.content.keys() else ''}\n```""" + + "=" * 10 + ) + prompt.append(instance_prompt) + return "\n".join(prompt) + + def get_referencer_prompt(doc_item: DocItem) -> str: + if len(doc_item.who_reference_me) == 0: + return "" + prompt = [ + """Also, the code has been called by the following objects, their code and docs are as following:""" + ] + for k, referencer_item in enumerate(doc_item.who_reference_me): + instance_prompt = ( + f"""obj: {referencer_item.get_full_name()}\nDocument: \n{referencer_item.md_content[-1] if len(referencer_item.md_content) > 0 else 'None'}\nRaw code:```\n{referencer_item.content['code_content'] if 'code_content' in referencer_item.content.keys() else 'None'}\n```""" + + "=" * 10 + ) + prompt.append(instance_prompt) + return "\n".join(prompt) + + def get_relationship_description(referencer_content, reference_letter): + if referencer_content and reference_letter: + has_relationship = "And please include the reference relationship with its callers and callees in the project from a functional perspective" + elif referencer_content: + return "And please include the relationship with its callers in the project from a functional perspective." + elif reference_letter: + return "And please include the relationship with its callees in the project from a functional perspective." + else: + return "" + + max_tokens = self.config.get("max_document_tokens", 1024) or 1024 + + language = self.config["language"] # setting document language + if language not in language_mapping: + raise KeyError( + f"Language code {language} is not provided! Supported languages are: {json.dumps(language_mapping)}" ) - prompt.append(instance_prompt) - return "\n".join(prompt) - - def get_relationship_description(referencer_content, reference_letter): - if referencer_content and reference_letter: - has_relationship = "And please include the reference relationship with its callers and callees in the project from a functional perspective" - elif referencer_content: - return "And please include the relationship with its callers in the project from a functional perspective." - elif reference_letter: - return "And please include the relationship with its callees in the project from a functional perspective." - else: - return "" - - - max_tokens = self.config.get("max_document_tokens", 1024) or 1024 - max_attempts = 5 # 设置最大尝试次数 - language = self.config["language"] # setting document language - if language not in language_mapping: - raise KeyError( - f"Language code {language} is not provided! Supported languages are: {json.dumps(language_mapping)}" + language = language_mapping[language] + + code_type_tell = "Class" if code_type == "ClassDef" else "Function" + parameters_or_attribute = ( + "attributes" if code_type == "ClassDef" else "parameters" + ) + have_return_tell = ( + "**Output Example**: Mock up a possible appearance of the code's return value." + if have_return + else "" ) - language = language_mapping[language] - - code_type_tell = "Class" if code_type == "ClassDef" else "Function" - parameters_or_attribute = ( - "attributes" if code_type == "ClassDef" else "parameters" - ) - have_return_tell = ( - "**Output Example**: Mock up a possible appearance of the code's return value." - if have_return - else "" - ) - # reference_letter = "This object is called in the following files, the file paths and corresponding calling parts of the code are as follows:" if referenced else "" - combine_ref_situation = ( - "and combine it with its calling situation in the project," - if referenced - else "" - ) - - referencer_content = get_referencer_prompt(doc_item) - reference_letter = get_referenced_prompt(doc_item) - has_relationship = get_relationship_description(referencer_content, reference_letter) - - project_structure_prefix = ", and the related hierarchical structure of this project is as follows (The current object is marked with an *):" - - # 第一次尝试构建完整的prompt - prompt_data = { - "combine_ref_situation": combine_ref_situation, - "file_path": file_path, - "project_structure_prefix": project_structure_prefix, - "project_structure": project_structure, - "code_type_tell": code_type_tell, - "code_name": code_name, - "code_content": code_content, - "have_return_tell": have_return_tell, - "has_relationship": has_relationship, - "reference_letter": reference_letter, - "referencer_content": referencer_content, - "parameters_or_attribute": parameters_or_attribute, - "language": language, - } - - sys_prompt = SYS_PROMPT.format(**prompt_data) - - usr_prompt = USR_PROMPT.format(language=language) - - # # 保存prompt到txt文件 - # with open(f'prompt_output/sys_prompt_{code_name}.txt', 'w', encoding='utf-8') as f: - # f.write(sys_prompt+'\n'+ usr_prompt) - - # logger.info(f"Using {max_input_tokens_map} for context window judgment.") - - model = self.config["default_completion_kwargs"]["model"] - max_input_length = max_input_tokens_map.get(model, 4096) - max_tokens - - total_tokens = ( - self.num_tokens_from_string(sys_prompt) + - self.num_tokens_from_string(usr_prompt) - ) - - # 如果总tokens超过当前模型的限制,则尝试寻找较大模型或者缩减输入 - if total_tokens >= max_input_length: - # 查找一个拥有更大输入限制的模型 - larger_models = {k: v for k, v in max_input_tokens_map.items() if (v - max_tokens) > total_tokens} # 抽取出所有上下文长度大于当前总输入tokens的模型 - for model_name, max_input_length in larger_models: - if max_input_length - max_tokens > total_tokens: - try: - # Attempt to make a request with the larger model - logger.info(f"Trying model {model_name} for large-context processing.") - response_message = self.attempt_generate_response(model_name, sys_prompt, usr_prompt, max_tokens) # response_message在attempt_generate_response中已经被校验过了 - - except Exception as e: - # 否值直接跳过,尝试下一个模型 - # logger.error(f"Model {model_name} failed with error: {e}") - continue # Try the next model - # If no larger models succeed, fallback to original model - # 对于最初的model模型,尝试缩减输入长度 - for shorten_attempt in range(2): - shorten_success = False - sys_prompt = self.reduce_input_length(shorten_attempt, prompt_data) - # 重新计算 tokens - total_tokens = ( - self.num_tokens_from_string(sys_prompt) + - self.num_tokens_from_string(usr_prompt) + # reference_letter = "This object is called in the following files, the file paths and corresponding calling parts of the code are as follows:" if referenced else "" + combine_ref_situation = ( + "and combine it with its calling situation in the project," + if referenced + else "" + ) + + referencer_content = get_referencer_prompt(doc_item) + reference_letter = get_referenced_prompt(doc_item) + has_relationship = get_relationship_description( + referencer_content, reference_letter + ) + + project_structure_prefix = ", and the related hierarchical structure of this project is as follows (The current object is marked with an *):" + + # 第一次尝试构建完整的prompt + prompt_data = { + "combine_ref_situation": combine_ref_situation, + "file_path": file_path, + "project_structure_prefix": project_structure_prefix, + "project_structure": project_structure, + "code_type_tell": code_type_tell, + "code_name": code_name, + "code_content": code_content, + "have_return_tell": have_return_tell, + "has_relationship": has_relationship, + "reference_letter": reference_letter, + "referencer_content": referencer_content, + "parameters_or_attribute": parameters_or_attribute, + "language": language, + } + + sys_prompt = SYS_PROMPT.format(**prompt_data) + + usr_prompt = USR_PROMPT.format(language=language) + + model = self.config["default_completion_kwargs"]["model"] + max_input_length = max_input_tokens_map.get(model, 4096) - max_tokens + + total_tokens = self.num_tokens_from_string( + sys_prompt + ) + self.num_tokens_from_string(usr_prompt) + + # 如果总tokens超过当前模型的限制,则尝试寻找较大模型或者缩减输入 + if total_tokens >= max_input_length: + # 查找一个拥有更大输入限制的模型 + larger_models = { + k: v + for k, v in max_input_tokens_map.items() + if (v - max_tokens) > total_tokens + } # 抽取出所有上下文长度大于当前总输入tokens的模型 + for model_name, max_input_length in larger_models: + if max_input_length - max_tokens > total_tokens: + try: + # Attempt to make a request with the larger model + logger.info( + f"Trying model {model_name} for large-context processing." + ) + response_message = self.attempt_generate_response( + model_name, sys_prompt, usr_prompt, max_tokens + ) # response_message在attempt_generate_response中已经被校验过了 + return response_message + except Exception as e: + # 否则直接跳过,尝试下一个模型 + continue # Try the next model + # If no larger models succeed, fallback to original model + # 对于最初的model模型,尝试缩减输入长度 + for shorten_attempt in range(2): + shorten_success = False + sys_prompt = self.reduce_input_length(shorten_attempt, prompt_data) + # 重新计算 tokens + total_tokens = self.num_tokens_from_string( + sys_prompt + ) + self.num_tokens_from_string(usr_prompt) + # 检查是否满足要求 + if total_tokens < max_input_length: + shorten_success = True + # 如满足要求直接发送请求来生成文档 + response_message = self.attempt_generate_response( + model, sys_prompt, usr_prompt, max_tokens + ) + + if not shorten_success: + # 意味着这个doc_item无法生成doc(因为代码本身的长度就超过了模型的限制) + # 返回一个自定义的response_message对象,它的content是"Tried to generate the document, but the code is too long to process." + # 在其他代码调用的时候使用的是response_message.content,所以必须确保content能通过这种方式从response_message中被读取出来 + response_message = ResponseMessage( + "Tried to generate the document, but the code is too long to process." + ) + return response_message + + else: # 如果总tokens没有超过模型限制,直接发送请求 + response_message = self.attempt_generate_response( + model, sys_prompt, usr_prompt, max_tokens ) - # 检查是否满足要求 - if total_tokens < max_input_length: - shorten_success = True - # 如满足要求直接发送请求来生成文档 - response_message = self.attempt_generate_response(model, sys_prompt, usr_prompt, max_tokens) - - - if not shorten_success: - # 意味着这个doc_item无法生成doc(因为代码本身的长度就超过了模型的限制) - # 返回一个自定义的response_message对象,它的content是"Tried to generate the document, but the code is too long to process." - # 在其他代码调用的时候使用的是response_message.content,所以必须确保content能通过这种方式从response_message中被读取出来 - response_message = ResponseMessage("Tried to generate the document, but the code is too long to process.") - return response_message - - else: # 如果总tokens没有超过模型限制,直接发送请求 - response_message = self.attempt_generate_response(model, sys_prompt, usr_prompt, max_tokens) - - return response_message + + return response_message diff --git a/repo_agent/doc_meta_info.py b/repo_agent/doc_meta_info.py index d75edd0..de9ca48 100644 --- a/repo_agent/doc_meta_info.py +++ b/repo_agent/doc_meta_info.py @@ -6,7 +6,6 @@ from typing import List, Dict, Any, Callable, Optional from colorama import Fore, Style from enum import Enum, unique, auto -import time from prettytable import PrettyTable import os import json diff --git a/repo_agent/exceptions.py b/repo_agent/exceptions.py index 287a716..fb3ca41 100644 --- a/repo_agent/exceptions.py +++ b/repo_agent/exceptions.py @@ -1,17 +1,16 @@ from openai import APIConnectionError from repo_agent.log import logger + + class ErrorHandler: @staticmethod def handle_exception(e): if isinstance(e, APIConnectionError): logger.warning(f"OpenAIResponseError occurred: {e}") - # Add your handling code here elif isinstance(e, OpenAIError): - print(f"OpenAIError occurred: {e}") - # Add your handling code here + logger.error(f"OpenAIError occurred: {e}") else: - print(f"An unexpected error occurred: {e}") - # Add your handling code here + logger.error(f"An unexpected error occurred: {e}") class OpenAIError(Exception): def __init__(self, message): diff --git a/repo_agent/file_handler.py b/repo_agent/file_handler.py index 65b0c05..b648df4 100644 --- a/repo_agent/file_handler.py +++ b/repo_agent/file_handler.py @@ -5,16 +5,15 @@ import ast from tqdm import tqdm from colorama import Fore, Style -import threading -from typing import Dict from repo_agent.utils.meta_info_utils import latest_verison_substring from repo_agent.config import CONFIG -from repo_agent.log import logger from repo_agent.utils.gitignore_checker import GitignoreChecker -# 这个类会在遍历变更后的文件的循环中,为每个变更后文件(也就是当前文件)创建一个实例 class FileHandler: + """ + 历变更后的文件的循环中,为每个变更后文件(也就是当前文件)创建一个实例 + """ def __init__(self, repo_path, file_path): self.file_path = file_path # 这里的file_path是相对于仓库根目录的路径 self.repo_path = repo_path diff --git a/repo_agent/log.py b/repo_agent/log.py index 147a6b6..2e45235 100644 --- a/repo_agent/log.py +++ b/repo_agent/log.py @@ -82,60 +82,3 @@ def set_logger_level_from_config(): # Automatically set logger level from config when module is imported set_logger_level_from_config() - - -# TODO 由 Loguru 接管 Print -# 重定向标准输出和错误输出, : -# - `stdout` 被重定向至 logger 的 INFO 等级。 -# - `stderr` 被重定向至 logger 的 ERROR 等级。 -# class StreamToLogger: -# """ -# Fake file-like stream object that redirects writes to a logger instance. - -# Args: -# logger (loguru.Logger): The logger instance to which the output will be redirected. -# level (str, optional): Log level for the messages. Defaults to 'INFO'. - -# Methods: -# write(buffer): Redirects the buffer to the logger. -# flush(): Dummy method to comply with file-like interface. -# """ - -# def __init__(self, logger, level="INFO"): -# self.logger = logger -# self._level = level - -# def write(self, buffer): -# for line in buffer.rstrip().splitlines(): -# self.logger.opt(depth=1).log(self._level, line.rstrip()) - -# def flush(self): -# pass - - -# # Redirect stdout to logger at success level -# sys.stdout = StreamToLogger(logger) - -# Redirect stderr to logger at error level -# sys.stderr = StreamToLogger(logger, "ERROR") - -# import stackprinter - - -# def handle_exception(exc_type, exc_value, exc_traceback): -# """ -# Custom exception handler using stackprinter for formatting. - -# Args: -# exc_type: Exception type. -# exc_value: Exception value. -# exc_traceback: Exception traceback. -# """ -# # Use stackprinter to format the exception and traceback -# formatted_exception = stackprinter.format(exc_value, exc_traceback, style='plaintext') - -# # Log the formatted exception -# logger.error("Unhandled exception:\n{}", formatted_exception) - -# # Set the custom exception handler -# sys.excepthook = handle_exception diff --git a/requirements.txt b/requirements.txt index fb82023..f58858c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -106,9 +106,9 @@ bcrypt==4.1.2 \ build==1.0.3 \ --hash=sha256:538aab1b64f9828977f84bc63ae570b060a8ed1be419e7870b8b4fc5e6ea553b \ --hash=sha256:589bf99a67df7c9cf07ec0ac0e5e2ea5d4b37ac63301c4986d1acb126aa83f8f -cachetools==5.3.2 \ - --hash=sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2 \ - --hash=sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1 +cachetools==5.3.3 \ + --hash=sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945 \ + --hash=sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105 certifi==2024.2.2 \ --hash=sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f \ --hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1 @@ -172,9 +172,9 @@ chroma-hnswlib==0.7.3 \ --hash=sha256:b7dca27b8896b494456db0fd705b689ac6b73af78e186eb6a42fea2de4f71c6f \ --hash=sha256:d71a3f4f232f537b6152947006bd32bc1629a8686df22fd97777b70f416c127a \ --hash=sha256:f96f4d5699e486eb1fb95849fe35ab79ab0901265805be7e60f4eaa83ce263ec -chromadb==0.4.23 \ - --hash=sha256:3d3c2ffb4ff560721e3daf8c1a3729fd149c551525b6f75543eddb81a4f29e16 \ - --hash=sha256:54d9a770640704c6cedc15317faab9fd45beb9833e7484c00037e7a8801a349f +chromadb==0.4.24 \ + --hash=sha256:3a08e237a4ad28b5d176685bd22429a03717fe09d35022fb230d516108da01da \ + --hash=sha256:a5c80b4e4ad9b236ed2d4899a5b9e8002b489293f2881cb2cadab5b199ee1c72 click==8.1.7 \ --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de @@ -237,9 +237,9 @@ distro==1.9.0 \ exceptiongroup==1.2.0; python_version < "3.11" \ --hash=sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14 \ --hash=sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68 -fastapi==0.109.2 \ - --hash=sha256:2c9bab24667293b501cad8dd388c05240c850b58ec5876ee3283c47d6e1e3a4d \ - --hash=sha256:f3817eac96fe4f65a2ebb4baa000f394e55f5fccdaf7f75250804bc58f354f73 +fastapi==0.110.0 \ + --hash=sha256:266775f0dcc95af9d3ef39bad55cff525329a931d5fd51930aadd4f428bf7ff3 \ + --hash=sha256:87a1f6fb632a218222c5984be540055346a8f5d8a68e8f6fb647b1dc9934de4b ffmpy==0.3.2 \ --hash=sha256:475ebfff1044661b8d969349dbcd2db9bf56d3ee78c0627e324769b49a27a78f filelock==3.13.1 \ @@ -434,9 +434,9 @@ httptools==0.6.1 \ httpx==0.27.0 \ --hash=sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5 \ --hash=sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5 -huggingface-hub==0.20.3 \ - --hash=sha256:94e7f8e074475fbc67d6a71957b678e1b4a74ff1b64a644fd6cbb83da962d05d \ - --hash=sha256:d988ae4f00d3e307b0c80c6a05ca6dbb7edba8bba3079f74cda7d9c2e562a7b6 +huggingface-hub==0.21.2 \ + --hash=sha256:16955c2b60bcff32a0778f84b9e9ae8f61d7f003da6aa1fbb7bc897a0c37b28c \ + --hash=sha256:839f2fc69fc51797b76dcffa7edbf7fb1150176f74cb1dc2d87ca00e5e0b5611 humanfriendly==10.0 \ --hash=sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477 \ --hash=sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc @@ -446,9 +446,9 @@ idna==3.6 \ importlib-metadata==6.11.0 \ --hash=sha256:1231cf92d825c9e03cfc4da076a16de6422c863558229ea0b22b675657463443 \ --hash=sha256:f0afba6205ad8f8947c7d338b5342d5db2afbfd82f9cbef7879a9539cc12eb9b -importlib-resources==6.1.1 \ - --hash=sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a \ - --hash=sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6 +importlib-resources==6.1.2 \ + --hash=sha256:308abf8474e2dba5f867d279237cd4076482c3de7104a40b41426370e891549b \ + --hash=sha256:9a0a862501dc38b68adebc82970140c9e4209fc99601782925178f8386339938 iniconfig==2.0.0 \ --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 @@ -576,9 +576,9 @@ markupsafe==2.1.5 \ --hash=sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b \ --hash=sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5 \ --hash=sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd -marshmallow==3.20.2 \ - --hash=sha256:4c1daff273513dc5eb24b219a8035559dc573c8f322558ef85f5438ddd1236dd \ - --hash=sha256:c21d4b98fee747c130e6bc8f45c4b3199ea66bc00c12ee1f639f0aeca034d5e9 +marshmallow==3.21.0 \ + --hash=sha256:20f53be28c6e374a711a16165fb22a8dc6003e3f7cda1285e3ca777b9193885b \ + --hash=sha256:e7997f83571c7fd476042c2c188e4ee8a78900ca5e74bd9c8097afa56624e9bd matplotlib==3.8.3 \ --hash=sha256:04b36ad07eac9740fc76c2aa16edf94e50b297d6eb4c081e3add863de4bb19a7 \ --hash=sha256:09074f8057917d17ab52c242fdf4916f30e99959c1908958b1fc6032e2d0f6d4 \ @@ -753,115 +753,115 @@ numpy==1.26.4 \ oauthlib==3.2.2 \ --hash=sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca \ --hash=sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918 -onnxruntime==1.17.0 \ - --hash=sha256:16d26badd092c8c257fa57c458bb600d96dc15282c647ccad0ed7b2732e6c03b \ - --hash=sha256:4b038324586bc905299e435f7c00007e6242389c856b82fe9357fdc3b1ef2bdc \ - --hash=sha256:6f1273bebcdb47ed932d076c85eb9488bc4768fcea16d5f2747ca692fad4f9d3 \ - --hash=sha256:7466724e809a40e986b1637cba156ad9fc0d1952468bc00f79ef340bc0199552 \ - --hash=sha256:90c0890e36f880281c6c698d9bc3de2afbeee2f76512725ec043665c25c67d21 \ - --hash=sha256:93d39b3fa1ee01f034f098e1c7769a811a21365b4883f05f96c14a2b60c6028b \ - --hash=sha256:ac2f286da3494b29b4186ca193c7d4e6a2c1f770c4184c7192c5da142c3dec28 \ - --hash=sha256:b4c87d83c6f58d1af2675fc99e3dc810f2dbdb844bcefd0c1b7573632661f6fc \ - --hash=sha256:bb1bf1ee575c665b8bbc3813ab906e091a645a24ccc210be7932154b8260eca1 \ - --hash=sha256:cb60fd3c2c1acd684752eb9680e89ae223e9801a9b0e0dc7b28adabe45a2e380 \ - --hash=sha256:d2b22a25a94109cc983443116da8d9805ced0256eb215c5e6bc6dcbabefeab96 \ - --hash=sha256:d47bee7557a8b99c8681b6882657a515a4199778d6d5e24e924d2aafcef55b0a \ - --hash=sha256:dba55723bf9b835e358f48c98a814b41692c393eb11f51e02ece0625c756b797 \ - --hash=sha256:ee48422349cc500273beea7607e33c2237909f58468ae1d6cccfc4aecd158565 \ - --hash=sha256:f34cc46553359293854e38bdae2ab1be59543aad78a6317e7746d30e311110c3 -openai==1.12.0 \ - --hash=sha256:99c5d257d09ea6533d689d1cc77caa0ac679fa21efef8893d8b0832a86877f1b \ - --hash=sha256:a54002c814e05222e413664f651b5916714e4700d041d5cf5724d3ae1a3e3481 -opentelemetry-api==1.22.0 \ - --hash=sha256:15ae4ca925ecf9cfdfb7a709250846fbb08072260fca08ade78056c502b86bed \ - --hash=sha256:43621514301a7e9f5d06dd8013a1b450f30c2e9372b8e30aaeb4562abf2ce034 -opentelemetry-exporter-otlp-proto-common==1.22.0 \ - --hash=sha256:3f2538bec5312587f8676c332b3747f54c89fe6364803a807e217af4603201fa \ - --hash=sha256:71ae2f81bc6d6fe408d06388826edc8933759b2ca3a97d24054507dc7cfce52d -opentelemetry-exporter-otlp-proto-grpc==1.22.0 \ - --hash=sha256:1e0e5aa4bbabc74942f06f268deffd94851d12a8dc30b02527472ef1729fe5b1 \ - --hash=sha256:b5bcadc129272004316a455e9081216d3380c1fc2231a928ea6a70aa90e173fb -opentelemetry-instrumentation==0.43b0 \ - --hash=sha256:0ff1334d7e359e27640e9d420024efeb73eacae464309c2e14ede7ba6c93967e \ - --hash=sha256:c3755da6c4be8033be0216d0501e11f4832690f4e2eca5a3576fbf113498f0f6 -opentelemetry-instrumentation-asgi==0.43b0 \ - --hash=sha256:1f593829fa039e9367820736fb063e92acd15c25b53d7bcb5d319971b8e93fd7 \ - --hash=sha256:3f6f19333dca31ef696672e4e36cb1c2613c71dc7e847c11ff36a37e1130dadc -opentelemetry-instrumentation-fastapi==0.43b0 \ - --hash=sha256:2afaaf470622e1a2732182c68f6d2431ffe5e026a7edacd0f83605632b66347f \ - --hash=sha256:b79c044df68a52e07b35fa12a424e7cc0dd27ff0a171c5fdcc41dea9de8fc938 -opentelemetry-proto==1.22.0 \ - --hash=sha256:9ec29169286029f17ca34ec1f3455802ffb90131642d2f545ece9a63e8f69003 \ - --hash=sha256:ce7188d22c75b6d0fe53e7fb58501613d0feade5139538e79dedd9420610fa0c -opentelemetry-sdk==1.22.0 \ - --hash=sha256:45267ac1f38a431fc2eb5d6e0c0d83afc0b78de57ac345488aa58c28c17991d0 \ - --hash=sha256:a730555713d7c8931657612a88a141e3a4fe6eb5523d9e2d5a8b1e673d76efa6 -opentelemetry-semantic-conventions==0.43b0 \ - --hash=sha256:291284d7c1bf15fdaddf309b3bd6d3b7ce12a253cec6d27144439819a15d8445 \ - --hash=sha256:b9576fb890df479626fa624e88dde42d3d60b8b6c8ae1152ad157a8b97358635 -opentelemetry-util-http==0.43b0 \ - --hash=sha256:3ff6ab361dbe99fc81200d625603c0fb890c055c6e416a3e6d661ddf47a6c7f7 \ - --hash=sha256:f25a820784b030f6cb86b3d76e5676c769b75ed3f55a210bcdae0a5e175ebadb -orjson==3.9.14 \ - --hash=sha256:06fb40f8e49088ecaa02f1162581d39e2cf3fd9dbbfe411eb2284147c99bad79 \ - --hash=sha256:08e722a8d06b13b67a51f247a24938d1a94b4b3862e40e0eef3b2e98c99cd04c \ - --hash=sha256:135d518f73787ce323b1a5e21fb854fe22258d7a8ae562b81a49d6c7f826f2a3 \ - --hash=sha256:19cdea0664aec0b7f385be84986d4defd3334e9c3c799407686ee1c26f7b8251 \ - --hash=sha256:1f7b6f3ef10ae8e3558abb729873d033dbb5843507c66b1c0767e32502ba96bb \ - --hash=sha256:20837e10835c98973673406d6798e10f821e7744520633811a5a3d809762d8cc \ - --hash=sha256:26280a7fcb62d8257f634c16acebc3bec626454f9ab13558bbf7883b9140760e \ - --hash=sha256:2eefc41ba42e75ed88bc396d8fe997beb20477f3e7efa000cd7a47eda452fbb2 \ - --hash=sha256:449bf090b2aa4e019371d7511a6ea8a5a248139205c27d1834bb4b1e3c44d936 \ - --hash=sha256:4dc1c132259b38d12c6587d190cd09cd76e3b5273ce71fe1372437b4cbc65f6f \ - --hash=sha256:58b36f54da759602d8e2f7dad958752d453dfe2c7122767bc7f765e17dc59959 \ - --hash=sha256:6f39a10408478f4c05736a74da63727a1ae0e83e3533d07b19443400fe8591ca \ - --hash=sha256:751250a31fef2bac05a2da2449aae7142075ea26139271f169af60456d8ad27a \ - --hash=sha256:793f6c9448ab6eb7d4974b4dde3f230345c08ca6c7995330fbceeb43a5c8aa5e \ - --hash=sha256:90903d2908158a2c9077a06f11e27545de610af690fb178fd3ba6b32492d4d1c \ - --hash=sha256:917311d6a64d1c327c0dfda1e41f3966a7fb72b11ca7aa2e7a68fcccc7db35d9 \ - --hash=sha256:95c03137b0cf66517c8baa65770507a756d3a89489d8ecf864ea92348e1beabe \ - --hash=sha256:9a1af21160a38ee8be3f4fcf24ee4b99e6184cadc7f915d599f073f478a94d2c \ - --hash=sha256:a2591faa0c031cf3f57e5bce1461cfbd6160f3f66b5a72609a130924917cb07d \ - --hash=sha256:a603161318ff699784943e71f53899983b7dee571b4dd07c336437c9c5a272b0 \ - --hash=sha256:a6bc7928d161840096adc956703494b5c0193ede887346f028216cac0af87500 \ - --hash=sha256:abcda41ecdc950399c05eff761c3de91485d9a70d8227cb599ad3a66afe93bcc \ - --hash=sha256:b7c11667421df2d8b18b021223505dcc3ee51be518d54e4dc49161ac88ac2b87 \ - --hash=sha256:c19009ff37f033c70acd04b636380379499dac2cba27ae7dfc24f304deabbc81 \ - --hash=sha256:ce6f095eef0026eae76fc212f20f786011ecf482fc7df2f4c272a8ae6dd7b1ef \ - --hash=sha256:d2cf1d0557c61c75e18cf7d69fb689b77896e95553e212c0cc64cf2087944b84 \ - --hash=sha256:d450a8e0656efb5d0fcb062157b918ab02dcca73278975b4ee9ea49e2fcf5bd5 \ - --hash=sha256:df76ecd17b1b3627bddfd689faaf206380a1a38cc9f6c4075bd884eaedcf46c2 \ - --hash=sha256:e2450d87dd7b4f277f4c5598faa8b49a0c197b91186c47a2c0b88e15531e4e3e \ - --hash=sha256:ea890e6dc1711aeec0a33b8520e395c2f3d59ead5b4351a788e06bf95fc7ba81 +onnxruntime==1.17.1 \ + --hash=sha256:2dff1a24354220ac30e4a4ce2fb1df38cb1ea59f7dac2c116238d63fe7f4c5ff \ + --hash=sha256:36fd6f87a1ecad87e9c652e42407a50fb305374f9a31d71293eb231caae18784 \ + --hash=sha256:40f08e378e0f85929712a2b2c9b9a9cc400a90c8a8ca741d1d92c00abec60843 \ + --hash=sha256:53e4e06c0a541696ebdf96085fd9390304b7b04b748a19e02cf3b35c869a1e76 \ + --hash=sha256:55b5e92a4c76a23981c998078b9bf6145e4fb0b016321a8274b1607bd3c6bd35 \ + --hash=sha256:5e3716b5eec9092e29a8d17aab55e737480487deabfca7eac3cd3ed952b6ada9 \ + --hash=sha256:606a7cbfb6680202b0e4f1890881041ffc3ac6e41760a25763bd9fe146f0b335 \ + --hash=sha256:6226a5201ab8cafb15e12e72ff2a4fc8f50654e8fa5737c6f0bd57c5ff66827e \ + --hash=sha256:99a8bddeb538edabc524d468edb60ad4722cff8a49d66f4e280c39eace70500b \ + --hash=sha256:ac79da6d3e1bb4590f1dad4bb3c2979d7228555f92bb39820889af8b8e6bd472 \ + --hash=sha256:ae9ba47dc099004e3781f2d0814ad710a13c868c739ab086fc697524061695ea \ + --hash=sha256:d43ac17ac4fa3c9096ad3c0e5255bb41fd134560212dc124e7f52c3159af5d21 \ + --hash=sha256:ebbcd2bc3a066cf54e6f18c75708eb4d309ef42be54606d22e5bdd78afc5b0d7 \ + --hash=sha256:fbb98cced6782ae1bb799cc74ddcbbeeae8819f3ad1d942a74d88e72b6511337 \ + --hash=sha256:fd7fddb4311deb5a7d3390cd8e9b3912d4d963efbe4dfe075edbaf18d01c024e +openai==1.13.3 \ + --hash=sha256:5769b62abd02f350a8dd1a3a242d8972c947860654466171d60fb0972ae0a41c \ + --hash=sha256:ff6c6b3bc7327e715e4b3592a923a5a1c7519ff5dd764a83d69f633d49e77a7b +opentelemetry-api==1.23.0 \ + --hash=sha256:14a766548c8dd2eb4dfc349739eb4c3893712a0daa996e5dbf945f9da665da9d \ + --hash=sha256:cc03ea4025353048aadb9c64919099663664672ea1c6be6ddd8fee8e4cd5e774 +opentelemetry-exporter-otlp-proto-common==1.23.0 \ + --hash=sha256:2a9e7e9d5a8b026b572684b6b24dcdefcaa58613d5ce3d644130b0c373c056c1 \ + --hash=sha256:35e4ea909e7a0b24235bd0aaf17fba49676527feb1823b46565ff246d5a1ab18 +opentelemetry-exporter-otlp-proto-grpc==1.23.0 \ + --hash=sha256:40f9e3e7761eb34f2a1001f4543028783ac26e2db27e420d5374f2cca0182dad \ + --hash=sha256:aa1a012eea5342bfef51fcf3f7f22601dcb0f0984a07ffe6025b2fbb6d91a2a9 +opentelemetry-instrumentation==0.44b0 \ + --hash=sha256:79560f386425176bcc60c59190064597096114c4a8e5154f1cb281bb4e47d2fc \ + --hash=sha256:8213d02d8c0987b9b26386ae3e091e0477d6331673123df736479322e1a50b48 +opentelemetry-instrumentation-asgi==0.44b0 \ + --hash=sha256:0d95c84a8991008c8a8ac35e15d43cc7768a5bb46f95f129e802ad2990d7c366 \ + --hash=sha256:72d4d28ec7ccd551eac11edc5ae8cac3586c0a228467d6a95fad7b6d4edd597a +opentelemetry-instrumentation-fastapi==0.44b0 \ + --hash=sha256:4441482944bea6676816668d56deb94af990e8c6e9582c581047e5d84c91d3c9 \ + --hash=sha256:67ed10b93ad9d35238ae0be73cf8acbbb65a4a61fb7444d0aee5b0c492e294db +opentelemetry-proto==1.23.0 \ + --hash=sha256:4c017deca052cb287a6003b7c989ed8b47af65baeb5d57ebf93dde0793f78509 \ + --hash=sha256:e6aaf8b7ace8d021942d546161401b83eed90f9f2cc6f13275008cea730e4651 +opentelemetry-sdk==1.23.0 \ + --hash=sha256:9ddf60195837b59e72fd2033d6a47e2b59a0f74f0ec37d89387d89e3da8cab7f \ + --hash=sha256:a93c96990ac0f07c6d679e2f1015864ff7a4f5587122dd5af968034436efb1fd +opentelemetry-semantic-conventions==0.44b0 \ + --hash=sha256:2e997cb28cd4ca81a25a9a43365f593d0c2b76be0685015349a89abdf1aa4ffa \ + --hash=sha256:7c434546c9cbd797ab980cc88bf9ff3f4a5a28f941117cad21694e43d5d92019 +opentelemetry-util-http==0.44b0 \ + --hash=sha256:75896dffcbbeb5df5429ad4526e22307fc041a27114e0c5bfd90bb219381e68f \ + --hash=sha256:ff018ab6a2fa349537ff21adcef99a294248b599be53843c44f367aef6bccea5 +orjson==3.9.15 \ + --hash=sha256:10c57bc7b946cf2efa67ac55766e41764b66d40cbd9489041e637c1304400494 \ + --hash=sha256:12365576039b1a5a47df01aadb353b68223da413e2e7f98c02403061aad34bde \ + --hash=sha256:2973474811db7b35c30248d1129c64fd2bdf40d57d84beed2a9a379a6f57d0ab \ + --hash=sha256:2c51378d4a8255b2e7c1e5cc430644f0939539deddfa77f6fac7b56a9784160a \ + --hash=sha256:2d99e3c4c13a7b0fb3792cc04c2829c9db07838fb6973e578b85c1745e7d0ce7 \ + --hash=sha256:2f256d03957075fcb5923410058982aea85455d035607486ccb847f095442bda \ + --hash=sha256:4228aace81781cc9d05a3ec3a6d2673a1ad0d8725b4e915f1089803e9efd2b99 \ + --hash=sha256:4feeb41882e8aa17634b589533baafdceb387e01e117b1ec65534ec724023d04 \ + --hash=sha256:5bb399e1b49db120653a31463b4a7b27cf2fbfe60469546baf681d1b39f4edf2 \ + --hash=sha256:62482873e0289cf7313461009bf62ac8b2e54bc6f00c6fabcde785709231a5d7 \ + --hash=sha256:6ae4e06be04dc00618247c4ae3f7c3e561d5bc19ab6941427f6d3722a0875ef7 \ + --hash=sha256:6f7b65bfaf69493c73423ce9db66cfe9138b2f9ef62897486417a8fcb0a92bfe \ + --hash=sha256:71c6b009d431b3839d7c14c3af86788b3cfac41e969e3e1c22f8a6ea13139404 \ + --hash=sha256:8055ec598605b0077e29652ccfe9372247474375e0e3f5775c91d9434e12d6b1 \ + --hash=sha256:82425dd5c7bd3adfe4e94c78e27e2fa02971750c2b7ffba648b0f5d5cc016a73 \ + --hash=sha256:87f1097acb569dde17f246faa268759a71a2cb8c96dd392cd25c668b104cad2f \ + --hash=sha256:946c3a1ef25338e78107fba746f299f926db408d34553b4754e90a7de1d44068 \ + --hash=sha256:95cae920959d772f30ab36d3b25f83bb0f3be671e986c72ce22f8fa700dae061 \ + --hash=sha256:9fe41b6f72f52d3da4db524c8653e46243c8c92df826ab5ffaece2dba9cccd58 \ + --hash=sha256:b3d336ed75d17c7b1af233a6561cf421dee41d9204aa3cfcc6c9c65cd5bb69a8 \ + --hash=sha256:b66bcc5670e8a6b78f0313bcb74774c8291f6f8aeef10fe70e910b8040f3ab75 \ + --hash=sha256:b725da33e6e58e4a5d27958568484aa766e825e93aa20c26c91168be58e08cbb \ + --hash=sha256:b72758f3ffc36ca566ba98a8e7f4f373b6c17c646ff8ad9b21ad10c29186f00d \ + --hash=sha256:bcef128f970bb63ecf9a65f7beafd9b55e3aaf0efc271a4154050fc15cdb386e \ + --hash=sha256:c8e8fe01e435005d4421f183038fc70ca85d2c1e490f51fb972db92af6e047c2 \ + --hash=sha256:d61f7ce4727a9fa7680cd6f3986b0e2c732639f46a5e0156e550e35258aa313a \ + --hash=sha256:d6768a327ea1ba44c9114dba5fdda4a214bdb70129065cd0807eb5f010bfcbb5 \ + --hash=sha256:e18668f1bd39e69b7fed19fa7cd1cd110a121ec25439328b5c89934e6d30d357 \ + --hash=sha256:fbbeb3c9b2edb5fd044b2a070f127a0ac456ffd079cb82746fc84af01ef021a4 \ + --hash=sha256:ff0f9913d82e1d1fadbd976424c316fbc4d9c525c81d047bbdd16bd27dd98cfc overrides==7.7.0 \ --hash=sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a \ --hash=sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49 packaging==23.2 \ --hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \ --hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7 -pandas==2.2.0 \ - --hash=sha256:159205c99d7a5ce89ecfc37cb08ed179de7783737cea403b295b5eda8e9c56d1 \ - --hash=sha256:20404d2adefe92aed3b38da41d0847a143a09be982a31b85bc7dd565bdba0f4e \ - --hash=sha256:2707514a7bec41a4ab81f2ccce8b382961a29fbe9492eab1305bb075b2b1ff4f \ - --hash=sha256:30b83f7c3eb217fb4d1b494a57a2fda5444f17834f5df2de6b2ffff68dc3c8e2 \ - --hash=sha256:38e0b4fc3ddceb56ec8a287313bc22abe17ab0eb184069f08fc6a9352a769b18 \ - --hash=sha256:5a946f210383c7e6d16312d30b238fd508d80d927014f3b33fb5b15c2f895430 \ - --hash=sha256:736da9ad4033aeab51d067fc3bd69a0ba36f5a60f66a527b3d72e2030e63280a \ - --hash=sha256:761cb99b42a69005dec2b08854fb1d4888fdf7b05db23a8c5a099e4b886a2106 \ - --hash=sha256:7ea3ee3f125032bfcade3a4cf85131ed064b4f8dd23e5ce6fa16473e48ebcaf5 \ - --hash=sha256:8108ee1712bb4fa2c16981fba7e68b3f6ea330277f5ca34fa8d557e986a11670 \ - --hash=sha256:85793cbdc2d5bc32620dc8ffa715423f0c680dacacf55056ba13454a5be5de88 \ - --hash=sha256:8ce2fbc8d9bf303ce54a476116165220a1fedf15985b09656b4b4275300e920b \ - --hash=sha256:a146b9dcacc3123aa2b399df1a284de5f46287a4ab4fbfc237eac98a92ebcb71 \ - --hash=sha256:a1b438fa26b208005c997e78672f1aa8138f67002e833312e6230f3e57fa87d5 \ - --hash=sha256:a20628faaf444da122b2a64b1e5360cde100ee6283ae8effa0d8745153809a2e \ - --hash=sha256:a41d06f308a024981dcaa6c41f2f2be46a6b186b902c94c2674e8cb5c42985bc \ - --hash=sha256:a626795722d893ed6aacb64d2401d017ddc8a2341b49e0384ab9bf7112bdec30 \ - --hash=sha256:cfd6c2491dc821b10c716ad6776e7ab311f7df5d16038d0b7458bc0b67dc10f3 \ - --hash=sha256:eb1e1f3861ea9132b32f2133788f3b14911b68102d562715d71bd0013bc45440 \ - --hash=sha256:f5be5d03ea2073627e7111f61b9f1f0d9625dc3c4d8dda72cc827b0c58a1d042 \ - --hash=sha256:f9670b3ac00a387620489dfc1bca66db47a787f4e55911f1293063a78b108df1 \ - --hash=sha256:fbc1b53c0e1fdf16388c33c3cca160f798d38aea2978004dd3f4d3dec56454c9 +pandas==2.2.1 \ + --hash=sha256:04f6ec3baec203c13e3f8b139fb0f9f86cd8c0b94603ae3ae8ce9a422e9f5bee \ + --hash=sha256:06cf591dbaefb6da9de8472535b185cba556d0ce2e6ed28e21d919704fef1a9e \ + --hash=sha256:0ab90f87093c13f3e8fa45b48ba9f39181046e8f3317d3aadb2fffbb1b978572 \ + --hash=sha256:0f573ab277252ed9aaf38240f3b54cfc90fff8e5cab70411ee1d03f5d51f3944 \ + --hash=sha256:101d0eb9c5361aa0146f500773395a03839a5e6ecde4d4b6ced88b7e5a1a6403 \ + --hash=sha256:11940e9e3056576ac3244baef2fedade891977bcc1cb7e5cc8f8cc7d603edc89 \ + --hash=sha256:4acf681325ee1c7f950d058b05a820441075b0dd9a2adf5c4835b9bc056bf4fb \ + --hash=sha256:53680dc9b2519cbf609c62db3ed7c0b499077c7fefda564e330286e619ff0dd9 \ + --hash=sha256:7d2ed41c319c9fb4fd454fe25372028dfa417aacb9790f68171b2e3f06eae8cd \ + --hash=sha256:88ecb5c01bb9ca927ebc4098136038519aa5d66b44671861ffab754cae75102c \ + --hash=sha256:8df8612be9cd1c7797c93e1c5df861b2ddda0b48b08f2c3eaa0702cf88fb5f88 \ + --hash=sha256:94e714a1cca63e4f5939cdce5f29ba8d415d85166be3441165edd427dc9f6bc0 \ + --hash=sha256:9d1265545f579edf3f8f0cb6f89f234f5e44ba725a34d86535b1a1d38decbccc \ + --hash=sha256:a935a90a76c44fe170d01e90a3594beef9e9a6220021acfb26053d01426f7dc2 \ + --hash=sha256:af5d3c00557d657c8773ef9ee702c61dd13b9d7426794c9dfeb1dc4a0bf0ebc7 \ + --hash=sha256:c2ce852e1cf2509a69e98358e8458775f89599566ac3775e70419b98615f4b06 \ + --hash=sha256:c38ce92cb22a4bea4e3929429aa1067a454dcc9c335799af93ba9be21b6beb51 \ + --hash=sha256:c391f594aae2fd9f679d419e9a4d5ba4bce5bb13f6a989195656e7dc4b95c8f0 \ + --hash=sha256:c70e00c2d894cb230e5c15e4b1e1e6b2b478e09cf27cc593a11ef955b9ecc81a \ + --hash=sha256:e97fbb5387c69209f134893abc788a6486dbf2f9e511070ca05eed4b930b1b02 \ + --hash=sha256:f02a3a6c83df4026e55b63c1f06476c9aa3ed6af3d89b4f04ea656ccdaaaa359 \ + --hash=sha256:f821213d48f4ab353d20ebc24e4faf94ba40d76680642fb7ce2ea31a3ad94f9b parso==0.8.3 \ --hash=sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0 \ --hash=sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75 @@ -955,65 +955,65 @@ pyasn1==0.5.1 \ pyasn1-modules==0.3.0 \ --hash=sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c \ --hash=sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d -pydantic==2.6.1 \ - --hash=sha256:0b6a909df3192245cb736509a92ff69e4fef76116feffec68e93a567347bae6f \ - --hash=sha256:4fd5c182a2488dc63e6d32737ff19937888001e2a6d86e94b3f233104a5d1fa9 -pydantic-core==2.16.2 \ - --hash=sha256:02906e7306cb8c5901a1feb61f9ab5e5c690dbbeaa04d84c1b9ae2a01ebe9379 \ - --hash=sha256:0ba503850d8b8dcc18391f10de896ae51d37fe5fe43dbfb6a35c5c5cad271a06 \ - --hash=sha256:16aa02e7a0f539098e215fc193c8926c897175d64c7926d00a36188917717a05 \ - --hash=sha256:22c5f022799f3cd6741e24f0443ead92ef42be93ffda0d29b2597208c94c3753 \ - --hash=sha256:2924b89b16420712e9bb8192396026a8fbd6d8726224f918353ac19c4c043d2a \ - --hash=sha256:308974fdf98046db28440eb3377abba274808bf66262e042c412eb2adf852731 \ - --hash=sha256:396fdf88b1b503c9c59c84a08b6833ec0c3b5ad1a83230252a9e17b7dfb4cffc \ - --hash=sha256:3ac426704840877a285d03a445e162eb258924f014e2f074e209d9b4ff7bf380 \ - --hash=sha256:3fab4e75b8c525a4776e7630b9ee48aea50107fea6ca9f593c98da3f4d11bf7c \ - --hash=sha256:406fac1d09edc613020ce9cf3f2ccf1a1b2f57ab00552b4c18e3d5276c67eb11 \ - --hash=sha256:40a0bd0bed96dae5712dab2aba7d334a6c67cbcac2ddfca7dbcc4a8176445990 \ - --hash=sha256:459c0d338cc55d099798618f714b21b7ece17eb1a87879f2da20a3ff4c7628e2 \ - --hash=sha256:459d6be6134ce3b38e0ef76f8a672924460c455d45f1ad8fdade36796df1ddc8 \ - --hash=sha256:46b0d5520dbcafea9a8645a8164658777686c5c524d381d983317d29687cce97 \ - --hash=sha256:47924039e785a04d4a4fa49455e51b4eb3422d6eaacfde9fc9abf8fdef164e8a \ - --hash=sha256:4bfcbde6e06c56b30668a0c872d75a7ef3025dc3c1823a13cf29a0e9b33f67e8 \ - --hash=sha256:4f9ee4febb249c591d07b2d4dd36ebcad0ccd128962aaa1801508320896575ef \ - --hash=sha256:5f60f920691a620b03082692c378661947d09415743e437a7478c309eb0e4f82 \ - --hash=sha256:60eb8ceaa40a41540b9acae6ae7c1f0a67d233c40dc4359c256ad2ad85bdf5e5 \ - --hash=sha256:6db58c22ac6c81aeac33912fb1af0e930bc9774166cdd56eade913d5f2fff35e \ - --hash=sha256:70651ff6e663428cea902dac297066d5c6e5423fda345a4ca62430575364d62b \ - --hash=sha256:72f7919af5de5ecfaf1eba47bf9a5d8aa089a3340277276e5636d16ee97614d7 \ - --hash=sha256:7b883af50eaa6bb3299780651e5be921e88050ccf00e3e583b1e92020333304b \ - --hash=sha256:7beec26729d496a12fd23cf8da9944ee338c8b8a17035a560b585c36fe81af20 \ - --hash=sha256:7bf26c2e2ea59d32807081ad51968133af3025c4ba5753e6a794683d2c91bf6e \ - --hash=sha256:7c31669e0c8cc68400ef0c730c3a1e11317ba76b892deeefaf52dcb41d56ed5d \ - --hash=sha256:870dbfa94de9b8866b37b867a2cb37a60c401d9deb4a9ea392abf11a1f98037b \ - --hash=sha256:88646cae28eb1dd5cd1e09605680c2b043b64d7481cdad7f5003ebef401a3039 \ - --hash=sha256:8aafeedb6597a163a9c9727d8a8bd363a93277701b7bfd2749fbefee2396469e \ - --hash=sha256:8bde5b48c65b8e807409e6f20baee5d2cd880e0fad00b1a811ebc43e39a00ab2 \ - --hash=sha256:8f9142a6ed83d90c94a3efd7af8873bf7cefed2d3d44387bf848888482e2d25f \ - --hash=sha256:936a787f83db1f2115ee829dd615c4f684ee48ac4de5779ab4300994d8af325b \ - --hash=sha256:98dc6f4f2095fc7ad277782a7c2c88296badcad92316b5a6e530930b1d475ebc \ - --hash=sha256:9957433c3a1b67bdd4c63717eaf174ebb749510d5ea612cd4e83f2d9142f3fc8 \ - --hash=sha256:99af961d72ac731aae2a1b55ccbdae0733d816f8bfb97b41909e143de735f522 \ - --hash=sha256:9b5f13857da99325dcabe1cc4e9e6a3d7b2e2c726248ba5dd4be3e8e4a0b6d0e \ - --hash=sha256:9d776d30cde7e541b8180103c3f294ef7c1862fd45d81738d156d00551005784 \ - --hash=sha256:a3b7352b48fbc8b446b75f3069124e87f599d25afb8baa96a550256c031bb890 \ - --hash=sha256:a477932664d9611d7a0816cc3c0eb1f8856f8a42435488280dfbf4395e141485 \ - --hash=sha256:a7e41e3ada4cca5f22b478c08e973c930e5e6c7ba3588fb8e35f2398cdcc1545 \ - --hash=sha256:a90fec23b4b05a09ad988e7a4f4e081711a90eb2a55b9c984d8b74597599180f \ - --hash=sha256:a9e523474998fb33f7c1a4d55f5504c908d57add624599e095c20fa575b8d943 \ - --hash=sha256:b0d7a9165167269758145756db43a133608a531b1e5bb6a626b9ee24bc38a8f7 \ - --hash=sha256:b94cbda27267423411c928208e89adddf2ea5dd5f74b9528513f0358bba019cb \ - --hash=sha256:ce232a6170dd6532096cadbf6185271e4e8c70fc9217ebe105923ac105da9978 \ - --hash=sha256:cf903310a34e14651c9de056fcc12ce090560864d5a2bb0174b971685684e1d8 \ - --hash=sha256:d5362d099c244a2d2f9659fb3c9db7c735f0004765bbe06b99be69fbd87c3f15 \ - --hash=sha256:dffaf740fe2e147fedcb6b561353a16243e654f7fe8e701b1b9db148242e1272 \ - --hash=sha256:e6294e76b0380bb7a61eb8a39273c40b20beb35e8c87ee101062834ced19c545 \ - --hash=sha256:eca58e319f4fd6df004762419612122b2c7e7d95ffafc37e890252f869f3fb2a \ - --hash=sha256:ed957db4c33bc99895f3a1672eca7e80e8cda8bd1e29a80536b4ec2153fa9804 \ - --hash=sha256:ef6113cd31411eaf9b39fc5a8848e71c72656fd418882488598758b2c8c6dfa0 \ - --hash=sha256:f8ed79883b4328b7f0bd142733d99c8e6b22703e908ec63d930b06be3a0e7113 \ - --hash=sha256:fe56851c3f1d6f5384b3051c536cc81b3a93a73faf931f404fef95217cf1e10d \ - --hash=sha256:ff7c97eb7a29aba230389a2661edf2e9e06ce616c7e35aa764879b6894a44b25 +pydantic==2.6.3 \ + --hash=sha256:72c6034df47f46ccdf81869fddb81aade68056003900a8724a4f160700016a2a \ + --hash=sha256:e07805c4c7f5c6826e33a1d4c9d47950d7eaf34868e2690f8594d2e30241f11f +pydantic-core==2.16.3 \ + --hash=sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a \ + --hash=sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed \ + --hash=sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979 \ + --hash=sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff \ + --hash=sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45 \ + --hash=sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340 \ + --hash=sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad \ + --hash=sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6 \ + --hash=sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241 \ + --hash=sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda \ + --hash=sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187 \ + --hash=sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba \ + --hash=sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c \ + --hash=sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2 \ + --hash=sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132 \ + --hash=sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf \ + --hash=sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db \ + --hash=sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade \ + --hash=sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4 \ + --hash=sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f \ + --hash=sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9 \ + --hash=sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec \ + --hash=sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb \ + --hash=sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4 \ + --hash=sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89 \ + --hash=sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da \ + --hash=sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac \ + --hash=sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b \ + --hash=sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e \ + --hash=sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137 \ + --hash=sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1 \ + --hash=sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b \ + --hash=sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e \ + --hash=sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053 \ + --hash=sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe \ + --hash=sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd \ + --hash=sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183 \ + --hash=sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8 \ + --hash=sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99 \ + --hash=sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975 \ + --hash=sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad \ + --hash=sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e \ + --hash=sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca \ + --hash=sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df \ + --hash=sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a \ + --hash=sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721 \ + --hash=sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f \ + --hash=sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97 \ + --hash=sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6 \ + --hash=sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc \ + --hash=sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1 \ + --hash=sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe \ + --hash=sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120 \ + --hash=sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f \ + --hash=sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a pydub==0.25.1 \ --hash=sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6 \ --hash=sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f @@ -1232,9 +1232,9 @@ ruff==0.2.2 \ semantic-version==2.10.0 \ --hash=sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c \ --hash=sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177 -setuptools==69.1.0 \ - --hash=sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401 \ - --hash=sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6 +setuptools==69.1.1 \ + --hash=sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56 \ + --hash=sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8 shellingham==1.5.4 \ --hash=sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686 \ --hash=sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de @@ -1244,9 +1244,9 @@ six==1.16.0 \ smmap==5.0.1 \ --hash=sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62 \ --hash=sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da -sniffio==1.3.0 \ - --hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \ - --hash=sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384 +sniffio==1.3.1 \ + --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ + --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc sqlalchemy==2.0.27 \ --hash=sha256:03f448ffb731b48323bda68bcc93152f751436ad6037f18a42b7e16af9e91c07 \ --hash=sha256:0de1263aac858f288a80b2071990f02082c51d88335a1db0d589237a3435fe71 \ @@ -1396,9 +1396,9 @@ tqdm==4.66.2 \ typer==0.9.0 \ --hash=sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2 \ --hash=sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee -typing-extensions==4.9.0 \ - --hash=sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783 \ - --hash=sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd +typing-extensions==4.10.0 \ + --hash=sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475 \ + --hash=sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb typing-inspect==0.9.0 \ --hash=sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f \ --hash=sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78 From 2fc925b712f6670981e4130298b92e6be5fd4cd2 Mon Sep 17 00:00:00 2001 From: Umpire2018 <138990495+Umpire2018@users.noreply.github.com> Date: Fri, 1 Mar 2024 15:49:34 +0000 Subject: [PATCH 04/11] feat: Enhance CLI with new features and refactor configuration access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Refactor configuration access via `pydantic-settings`, streamlining how configuration values are accessed and providing type validation. - Implement a new `language_prompt` function in the CLI tool, allowing users to input a language using ISO 639 codes or language names. This includes a retry mechanism using the tenacity library to handle invalid inputs, enhancing user experience and reliability. - Initially replace YAML configuration with TOML, improving the consistency and readability of the configuration files. This change includes integration with tomli for efficient config parsing and manipulation. - Introduce an automatic mechanism for setting configuration file paths, supporting both program root directory paths and hidden paths. - Introduce 'configure' commands to the CLI, enabling dynamic configuration updates and command execution. 临时提交 --- config.toml.template | 15 ++++ pdm.lock | 40 ++++++++++- pyproject.toml | 4 ++ repo_agent/chat_engine.py | 2 +- repo_agent/config_manager.py | 103 +++++++++++++++++++++++++++ repo_agent/log.py | 30 ++++---- repo_agent/main.py | 130 +++++++++++++++++++++++++++++++++++ repo_agent/settings.py | 72 +++++++++++++++++++ 8 files changed, 378 insertions(+), 18 deletions(-) create mode 100644 config.toml.template create mode 100644 repo_agent/config_manager.py create mode 100644 repo_agent/main.py create mode 100644 repo_agent/settings.py diff --git a/config.toml.template b/config.toml.template new file mode 100644 index 0000000..628caaa --- /dev/null +++ b/config.toml.template @@ -0,0 +1,15 @@ +[project] +target_repo = "" +hierarchy_name = ".project_doc_record" +markdown_docs_name = "markdown_docs" +ignore_list = [] +language = "Chinese" +max_thread_count = 4 +max_document_tokens = 1024 +log_level = "info" + +[chat_completion] +model = "gpt-3.5-turbo" +temperature = 0.2 +request_timeout = 60 +base_url = "https://api.openai.com/v1" diff --git a/pdm.lock b/pdm.lock index 160ab8e..b3c4bd3 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "test", "dev"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:2cba7560e488a60d1f3c00ba769bd000fe9ebec8210b9e9e18b9544710ff6635" +content_hash = "sha256:2a8bb1010ba8d612216131d444ae4ea34316fb63094ad4e87b885936f014ea70" [[package]] name = "aiofiles" @@ -2175,6 +2175,21 @@ files = [ {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"}, ] +[[package]] +name = "pydantic-settings" +version = "2.2.1" +requires_python = ">=3.8" +summary = "Settings management using Pydantic" +groups = ["default"] +dependencies = [ + "pydantic>=2.3.0", + "python-dotenv>=0.21.0", +] +files = [ + {file = "pydantic_settings-2.2.1-py3-none-any.whl", hash = "sha256:0235391d26db4d2190cb9b31051c4b46882d28a51533f97440867f012d4da091"}, + {file = "pydantic_settings-2.2.1.tar.gz", hash = "sha256:00b9f6a5e95553590434c0fa01ead0b216c3e10bc54ae02e37f359948643c5ed"}, +] + [[package]] name = "pydub" version = "0.25.1" @@ -2299,6 +2314,17 @@ files = [ {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, ] +[[package]] +name = "python-iso639" +version = "2024.2.7" +requires_python = ">=3.8" +summary = "Look-up utilities for ISO 639 language codes and names" +groups = ["default"] +files = [ + {file = "python-iso639-2024.2.7.tar.gz", hash = "sha256:c323233348c34d57c601e3e6d824088e492896bcb97a61a87f7d93401a305377"}, + {file = "python_iso639-2024.2.7-py3-none-any.whl", hash = "sha256:7b149623ff74230f4ee3061fb01d18e57a8d07c5fee2aa72907f39b7f6d16cbc"}, +] + [[package]] name = "python-multipart" version = "0.0.9" @@ -2904,12 +2930,22 @@ version = "2.0.1" requires_python = ">=3.7" summary = "A lil' TOML parser" groups = ["default", "test"] -marker = "python_version < \"3.11\"" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] +[[package]] +name = "tomli-w" +version = "1.0.0" +requires_python = ">=3.7" +summary = "A lil' TOML writer" +groups = ["default"] +files = [ + {file = "tomli_w-1.0.0-py3-none-any.whl", hash = "sha256:9f2a07e8be30a0729e533ec968016807069991ae2fd921a78d42f429ae5f4463"}, + {file = "tomli_w-1.0.0.tar.gz", hash = "sha256:f463434305e0336248cac9c2dc8076b707d8a12d019dd349f5c1e382dd1ae1b9"}, +] + [[package]] name = "tomlkit" version = "0.12.0" diff --git a/pyproject.toml b/pyproject.toml index a6bccaa..428ec12 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,10 @@ dependencies = [ "llama-index<0.10.0", "chromadb>=0.4.22", "prettytable>=3.9.0", + "python-iso639>=2024.2.7", + "pydantic-settings>=2.2.1", + "tomli>=2.0.1", + "tomli-w>=1.0.0", ] name = "repoagent" version = "0.0.6" diff --git a/repo_agent/chat_engine.py b/repo_agent/chat_engine.py index 985025a..4e6164f 100644 --- a/repo_agent/chat_engine.py +++ b/repo_agent/chat_engine.py @@ -1,6 +1,5 @@ import json import sys -from attr import dataclass import tiktoken import time import inspect @@ -10,6 +9,7 @@ from repo_agent.doc_meta_info import DocItem from repo_agent.log import logger from openai import OpenAI, APIConnectionError +from dataclasses import dataclass def get_import_statements(): diff --git a/repo_agent/config_manager.py b/repo_agent/config_manager.py new file mode 100644 index 0000000..cceef76 --- /dev/null +++ b/repo_agent/config_manager.py @@ -0,0 +1,103 @@ +# repo_agent/config_manager.py +import os +import tomli, tomli_w +from pathlib import Path +from loguru import logger +from typing import Dict + +def get_config_path() -> Path: + # 首先检查当前工作目录的父目录 + parent_directory = Path.cwd().parent + local_config_path = parent_directory / 'config.toml' + logger.debug(f'Checking local config path: {local_config_path}') + + # 如果在程序目录找到了 config.toml,则直接返回这个路径 + if local_config_path.exists(): + logger.debug(f'Found local config at: {local_config_path}') + return local_config_path + + # 如果在父目录没有找到 config.toml,按照原来的逻辑进行 + os_name = os.name + if os_name == 'posix': + # 对于 Unix 和 macOS,使用家目录 + home_config_path = Path.home() / '.repo_agent' + elif os_name == 'nt': + # 对于 Windows,使用 APPDATA 目录 + home_config_path = Path(os.getenv('APPDATA')) / 'repo_agent' + else: + # 如果操作系统检测失败,默认使用一个本地目录 + home_config_path = Path.cwd() / 'repo_agent' + + # 确保配置目录存在 + home_config_path.mkdir(parents=True, exist_ok=True) + config_path = home_config_path / 'config.toml' + logger.debug(f'Using home config path: {config_path}') + + # 确保配置文件存在,如果不存在则创建一个空文件 + if not config_path.exists(): + logger.debug(f'Config file does not exist, creating: {config_path}') + config_path.touch() + + # 返回包含文件名的完整路径 + return config_path + + +def read_config(file_path: Path | None = None) -> Dict[str, any]: + + if file_path is None: + file_path = get_config_path() + + logger.debug(f"Reading configuration from {file_path}") + + with open(file_path, "rb") as f: + try: + toml_dict = tomli.load(f) + except tomli.TOMLDecodeError: + toml_dict = {} + + return toml_dict + + + +def write_config(update_config: dict, file_path: Path | None = None) -> None: + if file_path is None: + file_path = get_config_path() + + # 先尝试读取现有配置,如果文件不存在则创建一个空的字典 + with open(file_path, "rb") as f: + try: + existing_config = tomli.load(f) + except tomli.TOMLDecodeError: + existing_config = {} + + # 更新配置:将新配置的键和值更新到现有配置字典中 + existing_config.update(update_config) + + # 将更新后的配置写回文件 + with open(file_path, "wb") as f: + tomli_w.dump(existing_config, f) + + logger.success("Configuration updated successfully.") + +if __name__ == '__main__': + # Sample TOML data to be written to the configuration file + sample_toml_data = """\ + val2 = 2 + val1 = 1 + + [table] + val3 = 4 + """ + # Convert the TOML string to a dictionary + sample_config = tomli.loads(sample_toml_data) + + # Write the TOML configuration to a file + write_config(sample_config) + + # Read the configuration back from the file + read_back_config = read_config() + + # Print the configuration to verify the contents + print(read_back_config) + + print(sample_config) \ No newline at end of file diff --git a/repo_agent/log.py b/repo_agent/log.py index 2e45235..45935f6 100644 --- a/repo_agent/log.py +++ b/repo_agent/log.py @@ -2,7 +2,15 @@ import sys import logging from loguru import logger -from repo_agent.config import CONFIG +from enum import StrEnum + +class LogLevel(StrEnum): + DEBUG = 'DEBUG' + INFO = 'INFO' + WARNING = 'WARNING' + ERROR = 'ERROR' + CRITICAL = 'CRITICAL' +# from repo_agent.config import CONFIG logger = logger.opt(colors=True) """ @@ -63,22 +71,14 @@ def emit(self, record): ) -def set_logger_level_from_config(): - log_level = CONFIG.get("log_level", "INFO").upper() +def set_logger_level_from_config(log_level): - try: - logger.remove() - logger.add(sys.stderr, level=log_level) + logger.remove() + logger.add(sys.stderr, level=log_level) - # Intercept standard logging - logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True) + # Intercept standard logging + logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True) - logger.success(f"Log level set to {log_level}!") - except ValueError: - logger.warning( - f"Invalid log level '{log_level}' in config. Using default log level." - ) + logger.success(f"Log level set to {log_level}!") -# Automatically set logger level from config when module is imported -set_logger_level_from_config() diff --git a/repo_agent/main.py b/repo_agent/main.py new file mode 100644 index 0000000..10bed04 --- /dev/null +++ b/repo_agent/main.py @@ -0,0 +1,130 @@ +import click +from repo_agent.settings import ( + Setting, + ProjectSettings, + ChatCompletionSettings, +) +from repo_agent.config_manager import write_config +from importlib import metadata +from loguru import logger +from tenacity import ( + retry, + stop_after_attempt, + retry_if_exception_type, + stop_after_attempt, +) +from iso639 import Language, LanguageNotFoundError +from repo_agent.log import LogLevel + +# 尝试获取版本号,如果失败,则使用默认版本号。 +try: + version_number = metadata.version("repoagent") +except metadata.PackageNotFoundError: + version_number = "0.0.0" + + +@retry( + retry=retry_if_exception_type(LanguageNotFoundError), + stop=stop_after_attempt(3), + retry_error_callback=lambda _: click.echo( + "Failed to find the language after several attempts." + ), +) +def language_prompt(default_language): + language_code = click.prompt( + "Enter the language (ISO 639 code or language name, e.g., 'en', 'eng', 'English')", + default=default_language, + ) + try: + language_name = Language.match(language_code).name + return language_name + except LanguageNotFoundError: + click.echo( + "Invalid language input. Please enter a valid ISO 639 code or language name." + ) + raise + + +@click.group() +@click.version_option(version_number) +def cli(): + """An LLM-powered repository agent designed to assist developers and teams in generating documentation and understanding repositories quickly.""" + pass + + +@cli.command() +def configure(): + """Configure the agent's parameters.""" + project_settings_default_instance = ProjectSettings() + project_settings_instance = ProjectSettings( + target_repo=click.prompt( + "Enter the path to target repository", + type=click.Path(exists=True, file_okay=False, dir_okay=True), + ), + hierarchy_name=click.prompt( + "Enter the project hierarchy file name", + default=project_settings_default_instance.hierarchy_name, + ), + markdown_docs_name=click.prompt( + "Enter the Markdown documents folder name", + default=project_settings_default_instance.markdown_docs_name, + ), + ignore_list=click.prompt( + "Enter files or directories to ignore, separated by commas", + default=",".join(project_settings_default_instance.ignore_list), + ).split(","), + language=language_prompt( + default_language=project_settings_default_instance.language + ), + max_thread_count=click.prompt( + "Enter the maximum number of threads", + default=project_settings_default_instance.max_thread_count, + type=click.INT, + ), + max_document_tokens=click.prompt( + "Enter the maximum number of document tokens", + default=project_settings_default_instance.max_document_tokens, + type=click.INT, + ), + log_level=click.prompt( + "Enter the log level", + type=click.Choice([level.value for level in LogLevel], case_sensitive=False), + default=project_settings_default_instance.log_level.value + ), + ) + + logger.info("Project settings saved successfully.") + + chat_completion_default_instance = ChatCompletionSettings() + + chat_completion_instance = ChatCompletionSettings( + model=click.prompt( + "Enter the model", default=chat_completion_default_instance.model + ), + temperature=click.prompt( + "Enter the temperature", + default=chat_completion_default_instance.temperature, + type=float, + ), + request_timeout=click.prompt( + "Enter the request timeout (seconds)", + default=chat_completion_default_instance.request_timeout, + type=int, + ), + base_url=click.prompt( + "Enter the base URL", default=str(chat_completion_default_instance.base_url) + ), + ) + logger.info("Chat completion settings saved successfully.") + + update_setting = Setting( + project=project_settings_instance, chat_completion=chat_completion_instance + ) + + logger.debug(f"Current settings: {update_setting.model_dump()}") + + write_config(update_setting.model_dump()) + + +if __name__ == "__main__": + cli() diff --git a/repo_agent/settings.py b/repo_agent/settings.py new file mode 100644 index 0000000..2427bd6 --- /dev/null +++ b/repo_agent/settings.py @@ -0,0 +1,72 @@ +from pydantic_settings import BaseSettings +from pydantic import ( + PositiveFloat, + HttpUrl, + PositiveInt, + DirectoryPath, + SecretStr, + field_validator, + field_serializer, + Field, +) +from repo_agent.config_manager import read_config, write_config +from iso639 import Language, LanguageNotFoundError +from repo_agent.log import LogLevel, set_logger_level_from_config + +class ProjectSettings(BaseSettings): + target_repo: DirectoryPath = "" + hierarchy_name: str = ".project_doc_record" + markdown_docs_name: str = "markdown_docs" + ignore_list: list[str] = [] + language: str = "Chinese" + max_thread_count: PositiveInt = 4 + max_document_tokens: PositiveInt = 1024 + log_level: LogLevel = Field(default=LogLevel.INFO) + + @field_validator("language") + @classmethod + def validate_language_code(cls, v: str) -> str: + try: + language_name = Language.match(v).name + return language_name # Returning the resolved language name + except LanguageNotFoundError: + raise ValueError( + "Invalid language input. Please enter a valid ISO 639 code or language name." + ) + + @field_validator("log_level", mode='before') + @classmethod + def set_log_level(cls, v: str) -> LogLevel: + if isinstance(v, str): + v = v.upper() # 将输入转换为大写 + if v in LogLevel._value2member_map_: # 检查转换后的值是否在枚举成员中 + return LogLevel(v) + raise ValueError(f'Invalid log level: {v}') + + @field_serializer('target_repo') + def serialize_target_repo(self, target_repo: DirectoryPath): + return str(target_repo) + +class ChatCompletionSettings(BaseSettings): + model: str = "gpt-3.5-turbo" + temperature: PositiveFloat = 0.2 + request_timeout: PositiveInt = 60 + base_url: HttpUrl = "https://api.openai.com/v1" + openai_api_key: SecretStr = Field(..., exclude=True) + + + @field_serializer('base_url') + def serialize_base_url(self, base_url: HttpUrl): + return str(base_url) + +class Setting(BaseSettings): + project: ProjectSettings = {} + chat_completion: ChatCompletionSettings = {} + + +_config_data = read_config() +setting = Setting.model_validate(_config_data) +set_logger_level_from_config(log_level=setting.project.log_level) + +if _config_data == {}: + write_config(setting.model_dump()) \ No newline at end of file From e6edb5c1d85a12d4f8760d5b3c69a4bd848f7a53 Mon Sep 17 00:00:00 2001 From: Umpire2018 <138990495+Umpire2018@users.noreply.github.com> Date: Sat, 2 Mar 2024 09:12:21 +0000 Subject: [PATCH 05/11] feat: add `run` command with detailed options and short flags - Remove redundant import. - Adjust the `max_input_tokens_map` within `settings.py`. - Removed the static `language_mapping` dictionary from `config.py`. This change paves the way for a more robust and scalable solution for multilingual support within the application. --- pyproject.toml | 3 + repo_agent/__main__.py | 1 - repo_agent/chat_engine.py | 32 +++----- repo_agent/config.py | 53 ------------ repo_agent/main.py | 132 ++++++++++++++++++++++++++++-- repo_agent/multi_task_dispatch.py | 2 - repo_agent/project_manager.py | 2 - repo_agent/runner.py | 6 +- repo_agent/settings.py | 54 ++++++++---- 9 files changed, 178 insertions(+), 107 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 428ec12..271c13c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,3 +54,6 @@ includes = [ requires = ["pdm-backend"] build-backend = "pdm.backend" + +[tool.pyright] +reportCallIssue="none" \ No newline at end of file diff --git a/repo_agent/__main__.py b/repo_agent/__main__.py index 94bb13d..9d6b066 100755 --- a/repo_agent/__main__.py +++ b/repo_agent/__main__.py @@ -1,4 +1,3 @@ -#!/Users/yeyn/anaconda3/envs/ai-doc/bin/python import os import sys diff --git a/repo_agent/chat_engine.py b/repo_agent/chat_engine.py index 4e6164f..0dff294 100644 --- a/repo_agent/chat_engine.py +++ b/repo_agent/chat_engine.py @@ -1,17 +1,15 @@ -import json import sys import tiktoken import time import inspect from repo_agent.log import logger -from repo_agent.config import language_mapping, max_input_tokens_map +from repo_agent.settings import setting, max_input_tokens_map from repo_agent.prompt import SYS_PROMPT, USR_PROMPT from repo_agent.doc_meta_info import DocItem from repo_agent.log import logger from openai import OpenAI, APIConnectionError from dataclasses import dataclass - def get_import_statements(): source_lines = inspect.getsourcelines(sys.modules[__name__])[0] import_lines = [ @@ -31,8 +29,7 @@ class ChatEngine: ChatEngine is used to generate the doc of functions or classes. """ - def __init__(self, CONFIG, project_manager): - self.config = CONFIG + def __init__(self, project_manager): self.project_manager = project_manager def num_tokens_from_string(self, string: str, encoding_name="cl100k_base") -> int: @@ -70,9 +67,9 @@ def reduce_input_length(self, shorten_attempt, prompt_data): def generate_response(self, model, sys_prompt, usr_prompt, max_tokens): client = OpenAI( - api_key=self.config["api_keys"][model][0]["api_key"], - base_url=self.config["api_keys"][model][0]["base_url"], - timeout=self.config["default_completion_kwargs"]["request_timeout"], + api_key=setting.chat_completion.openai_api_key.get_secret_value(), + base_url=str(setting.chat_completion.base_url), + timeout=setting.chat_completion.request_timeout, ) messages = [ @@ -83,7 +80,7 @@ def generate_response(self, model, sys_prompt, usr_prompt, max_tokens): response = client.chat.completions.create( model=model, messages=messages, - temperature=self.config["default_completion_kwargs"]["temperature"], + temperature=setting.chat_completion.temperature, max_tokens=max_tokens, ) @@ -188,7 +185,7 @@ def get_referencer_prompt(doc_item: DocItem) -> str: def get_relationship_description(referencer_content, reference_letter): if referencer_content and reference_letter: - has_relationship = "And please include the reference relationship with its callers and callees in the project from a functional perspective" + return "And please include the reference relationship with its callers and callees in the project from a functional perspective" elif referencer_content: return "And please include the relationship with its callers in the project from a functional perspective." elif reference_letter: @@ -196,14 +193,7 @@ def get_relationship_description(referencer_content, reference_letter): else: return "" - max_tokens = self.config.get("max_document_tokens", 1024) or 1024 - - language = self.config["language"] # setting document language - if language not in language_mapping: - raise KeyError( - f"Language code {language} is not provided! Supported languages are: {json.dumps(language_mapping)}" - ) - language = language_mapping[language] + max_tokens = setting.chat_completion.max_document_tokens code_type_tell = "Class" if code_type == "ClassDef" else "Function" parameters_or_attribute = ( @@ -243,14 +233,14 @@ def get_relationship_description(referencer_content, reference_letter): "reference_letter": reference_letter, "referencer_content": referencer_content, "parameters_or_attribute": parameters_or_attribute, - "language": language, + "language": setting.project.language, } sys_prompt = SYS_PROMPT.format(**prompt_data) - usr_prompt = USR_PROMPT.format(language=language) + usr_prompt = USR_PROMPT.format(language=setting.project.language) - model = self.config["default_completion_kwargs"]["model"] + model = setting.chat_completion.model max_input_length = max_input_tokens_map.get(model, 4096) - max_tokens total_tokens = self.num_tokens_from_string( diff --git a/repo_agent/config.py b/repo_agent/config.py index 614004f..d7d461d 100644 --- a/repo_agent/config.py +++ b/repo_agent/config.py @@ -9,56 +9,3 @@ ) sys.exit(1) # Exits the program -language_mapping = { - "en": "English", - "es": "Spanish", - "fr": "French", - "de": "German", - "zh": "Chinese", - "ja": "Japanese", - "ru": "Russian", - "it": "Italian", - "ko": "Korean", - "nl": "Dutch", - "pt": "Portuguese", - "ar": "Arabic", - "tr": "Turkish", - "sv": "Swedish", - "da": "Danish", - "fi": "Finnish", - "no": "Norwegian", - "pl": "Polish", - "cs": "Czech", - "hu": "Hungarian", - "el": "Greek", - "he": "Hebrew", - "th": "Thai", - "hi": "Hindi", - "bn": "Bengali", -} - -# 获取 yaml 文件中实际使用的模型列表 -used_models = [] -for model_name, _ in CONFIG['api_keys'].items(): - used_models.append(model_name) - -# NOTE Each model's token limit has been reduced by 1024 tokens to account for the output space and 1 for boundary conditions. -max_input_tokens_map = { - "gpt-3.5-turbo": 4096, # NOTE OPENAI said that The gpt-3.5-turbo model alias will be automatically upgraded from gpt-3.5-turbo-0613 to gpt-3.5-turbo-0125 on February 16th. But in 2/20, then still maintain 4,096 tokens for context window. - "gpt-3.5-turbo-0613": 4096, # NOTE Will be deprecated on June 13, 2024. - "gpt-3.5-turbo-16k": 16384, # NOTE Will be deprecated on June 13, 2024. - "gpt-3.5-turbo-16k-0613": 16384, # NOTE Will be deprecated on June 13, 2024. - "gpt-3.5-turbo-0125": 16384, - "gpt-4": 8192, - "gpt-4-0613": 8192, - "gpt-4-32k": 32768, # NOTE This model was never rolled out widely in favor of GPT-4 Turbo. - "gpt-4-1106": 131072, - "gpt-4-0125-preview": 131072, - "gpt-4-turbo-preview": 131072, -} - -# 移除在 yaml 文件中未使用的模型 -for model_key in list(max_input_tokens_map.keys()): - if model_key not in used_models: - del max_input_tokens_map[model_key] - diff --git a/repo_agent/main.py b/repo_agent/main.py index 10bed04..9d9ff86 100644 --- a/repo_agent/main.py +++ b/repo_agent/main.py @@ -14,7 +14,7 @@ stop_after_attempt, ) from iso639 import Language, LanguageNotFoundError -from repo_agent.log import LogLevel +from repo_agent.log import LogLevel # 尝试获取版本号,如果失败,则使用默认版本号。 try: @@ -22,6 +22,9 @@ except metadata.PackageNotFoundError: version_number = "0.0.0" +project_settings_default_instance = ProjectSettings() +chat_completion_default_instance = ChatCompletionSettings() + @retry( retry=retry_if_exception_type(LanguageNotFoundError), @@ -55,7 +58,6 @@ def cli(): @cli.command() def configure(): """Configure the agent's parameters.""" - project_settings_default_instance = ProjectSettings() project_settings_instance = ProjectSettings( target_repo=click.prompt( "Enter the path to target repository", @@ -88,15 +90,15 @@ def configure(): ), log_level=click.prompt( "Enter the log level", - type=click.Choice([level.value for level in LogLevel], case_sensitive=False), - default=project_settings_default_instance.log_level.value + type=click.Choice( + [level.value for level in LogLevel], case_sensitive=False + ), + default=project_settings_default_instance.log_level.value, ), ) logger.info("Project settings saved successfully.") - chat_completion_default_instance = ChatCompletionSettings() - chat_completion_instance = ChatCompletionSettings( model=click.prompt( "Enter the model", default=chat_completion_default_instance.model @@ -126,5 +128,123 @@ def configure(): write_config(update_setting.model_dump()) +@cli.command() +@click.option( + "--model", + "-m", + default=chat_completion_default_instance.model, + show_default=True, + help="Specifies the model to use for completion.", + type=str, +) +@click.option( + "--temperature", + "-t", + default=chat_completion_default_instance.temperature, + show_default=True, + help="Sets the generation temperature for the model. Lower values make the model more deterministic.", + type=float, +) +@click.option( + "--request-timeout", + "-r", + default=chat_completion_default_instance.request_timeout, + show_default=True, + help="Defines the timeout in seconds for the API request.", + type=int, +) +@click.option( + "--base-url", + "-b", + default=chat_completion_default_instance.base_url, + show_default=True, + help="The base URL for the API calls.", + type=str, +) +@click.option( + "--target-repo-path", + "-tp", + default=project_settings_default_instance.target_repo, + show_default=True, + help="The file system path to the target repository. This path is used as the root for documentation generation.", + type=click.Path(), +) +@click.option( + "--hierarchy-path", + "-hp", + default=project_settings_default_instance.hierarchy_name, + show_default=True, + help="The name or path for the project hierarchy file, used to organize documentation structure.", + type=str, +) +@click.option( + "--markdown-docs-path", + "-mdp", + default=project_settings_default_instance.markdown_docs_name, + show_default=True, + help="The folder path where Markdown documentation will be stored or generated.", + type=str, +) +@click.option( + "--ignore-list", + "-i", + default=project_settings_default_instance.ignore_list, + show_default=True, + help="A list of files or directories to ignore during documentation generation, separated by commas.", + multiple=True, + type=str, +) +@click.option( + "--language", + "-l", + default=project_settings_default_instance.language, + show_default=True, + help="The ISO 639 code or language name for the documentation. ", + type=str, +) +@click.option( + "--log-level", + "-ll", + default=project_settings_default_instance.log_level, + show_default=True, + help="Sets the logging level (e.g., DEBUG, INFO, WARNING, ERROR, CRITICAL) for the application. Default is INFO.", + type=click.Choice([level.value for level in LogLevel], case_sensitive=False), +) +def run( + model, + temperature, + request_timeout, + base_url, + target_repo_path, + hierarchy_path, + markdown_docs_path, + ignore_list, + language, + log_level, +): + """Run the program with the specified parameters.""" + + project_settings = ProjectSettings( + target_repo=target_repo_path, + hierarchy_name=hierarchy_path, + markdown_docs_name=markdown_docs_path, + ignore_list=list(ignore_list), # convert tuple from 'multiple' option to list + language=language, + log_level=log_level, + ) + + chat_completion_settings = ChatCompletionSettings( + model=model, + temperature=temperature, + request_timeout=request_timeout, + base_url=base_url, + ) + + settings = Setting( + project=project_settings, chat_completion=chat_completion_settings + ) + write_config(settings.model_dump()) + + if __name__ == "__main__": cli() diff --git a/repo_agent/multi_task_dispatch.py b/repo_agent/multi_task_dispatch.py index ecbb28e..1c22e43 100644 --- a/repo_agent/multi_task_dispatch.py +++ b/repo_agent/multi_task_dispatch.py @@ -5,8 +5,6 @@ from typing import List, Callable, Dict, Any from colorama import Fore, Style -from repo_agent.log import logger - class Task: def __init__(self, task_id: int, dependencies: List[Task], extra_info: Any = None): diff --git a/repo_agent/project_manager.py b/repo_agent/project_manager.py index 0b67474..00a4439 100644 --- a/repo_agent/project_manager.py +++ b/repo_agent/project_manager.py @@ -1,7 +1,5 @@ import os import jedi -from collections import defaultdict - class ProjectManager: diff --git a/repo_agent/runner.py b/repo_agent/runner.py index fec5272..5ac5835 100644 --- a/repo_agent/runner.py +++ b/repo_agent/runner.py @@ -1,8 +1,6 @@ import threading import os, json import json -import git -import itertools from tqdm import tqdm from typing import List from functools import partial @@ -13,11 +11,11 @@ import time from repo_agent.file_handler import FileHandler -from repo_agent.utils.meta_info_utils import latest_verison_substring, make_fake_files, delete_fake_files +from repo_agent.utils.meta_info_utils import make_fake_files, delete_fake_files from repo_agent.change_detector import ChangeDetector from repo_agent.project_manager import ProjectManager from repo_agent.chat_engine import ChatEngine -from repo_agent.doc_meta_info import MetaInfo, DocItem, DocItemType, DocItemStatus, need_to_generate +from repo_agent.doc_meta_info import MetaInfo, DocItem, DocItemStatus, need_to_generate from repo_agent.log import logger from repo_agent.config import CONFIG from repo_agent.multi_task_dispatch import worker diff --git a/repo_agent/settings.py b/repo_agent/settings.py index 2427bd6..d23684c 100644 --- a/repo_agent/settings.py +++ b/repo_agent/settings.py @@ -13,15 +13,16 @@ from iso639 import Language, LanguageNotFoundError from repo_agent.log import LogLevel, set_logger_level_from_config + class ProjectSettings(BaseSettings): - target_repo: DirectoryPath = "" + target_repo: DirectoryPath = "" # type: ignore hierarchy_name: str = ".project_doc_record" markdown_docs_name: str = "markdown_docs" ignore_list: list[str] = [] language: str = "Chinese" max_thread_count: PositiveInt = 4 max_document_tokens: PositiveInt = 1024 - log_level: LogLevel = Field(default=LogLevel.INFO) + log_level: LogLevel = LogLevel.INFO @field_validator("language") @classmethod @@ -33,35 +34,37 @@ def validate_language_code(cls, v: str) -> str: raise ValueError( "Invalid language input. Please enter a valid ISO 639 code or language name." ) - - @field_validator("log_level", mode='before') + + @field_validator("log_level", mode="before") @classmethod def set_log_level(cls, v: str) -> LogLevel: - if isinstance(v, str): - v = v.upper() # 将输入转换为大写 - if v in LogLevel._value2member_map_: # 检查转换后的值是否在枚举成员中 - return LogLevel(v) - raise ValueError(f'Invalid log level: {v}') + if isinstance(v, str): + v = v.upper() # 将输入转换为大写 + if v in LogLevel._value2member_map_: # 检查转换后的值是否在枚举成员中 + return LogLevel(v) + raise ValueError(f"Invalid log level: {v}") - @field_serializer('target_repo') + @field_serializer("target_repo") def serialize_target_repo(self, target_repo: DirectoryPath): return str(target_repo) + class ChatCompletionSettings(BaseSettings): model: str = "gpt-3.5-turbo" temperature: PositiveFloat = 0.2 - request_timeout: PositiveInt = 60 - base_url: HttpUrl = "https://api.openai.com/v1" + request_timeout: PositiveFloat = 60.0 + base_url: HttpUrl = "https://api.openai.com/v1" # type: ignore + max_document_tokens: PositiveInt = 1024 openai_api_key: SecretStr = Field(..., exclude=True) - - @field_serializer('base_url') + @field_serializer("base_url") def serialize_base_url(self, base_url: HttpUrl): return str(base_url) - + + class Setting(BaseSettings): - project: ProjectSettings = {} - chat_completion: ChatCompletionSettings = {} + project: ProjectSettings = {} # type: ignore + chat_completion: ChatCompletionSettings = {} # type: ignore _config_data = read_config() @@ -69,4 +72,19 @@ class Setting(BaseSettings): set_logger_level_from_config(log_level=setting.project.log_level) if _config_data == {}: - write_config(setting.model_dump()) \ No newline at end of file + write_config(setting.model_dump()) + +# NOTE Each model's token limit has been reduced by 1024 tokens to account for the output space and 1 for boundary conditions. +max_input_tokens_map = { + "gpt-3.5-turbo": 4096, # NOTE OPENAI said that The gpt-3.5-turbo model alias will be automatically upgraded from gpt-3.5-turbo-0613 to gpt-3.5-turbo-0125 on February 16th. But in 2/20, then still maintain 4,096 tokens for context window. + "gpt-3.5-turbo-0613": 4096, # NOTE Will be deprecated on June 13, 2024. + "gpt-3.5-turbo-16k": 16384, # NOTE Will be deprecated on June 13, 2024. + "gpt-3.5-turbo-16k-0613": 16384, # NOTE Will be deprecated on June 13, 2024. + "gpt-3.5-turbo-0125": 16384, + "gpt-4": 8192, + "gpt-4-0613": 8192, + "gpt-4-32k": 32768, # NOTE This model was never rolled out widely in favor of GPT-4 Turbo. + "gpt-4-1106": 131072, + "gpt-4-0125-preview": 131072, + "gpt-4-turbo-preview": 131072, +} From 0bf63d3655907fba703fe232c6a15fe88a73d34a Mon Sep 17 00:00:00 2001 From: Umpire2018 <138990495+Umpire2018@users.noreply.github.com> Date: Tue, 5 Mar 2024 15:14:31 +0000 Subject: [PATCH 06/11] refactor: streamline configuration variable access - Replace YAML-based configuration handling with pydantic-settings and TOML for enhanced reliability and ease of use. - Refactor file path operations to use pathlib.Path, adopting a more modern and object-oriented approach. - Rebuild the CLI entry point using Click to improve maintainability and user experience. - Version bumped to 0.1.0 to mark the significant updates in configuration management and CLI design. --- .gitignore | 5 +- README.md | 1 - config.yml.template | 51 ----- pdm.lock | 183 +++++++++--------- pyproject.toml | 6 +- repo_agent/__main__.py | 40 +--- repo_agent/change_detector.py | 22 ++- repo_agent/chat_engine.py | 19 +- repo_agent/chat_with_repo/__init__.py | 0 repo_agent/chat_with_repo/gradio_interface.py | 1 + repo_agent/chat_with_repo/json_handler.py | 3 +- repo_agent/chat_with_repo/main.py | 12 +- repo_agent/chat_with_repo/prompt.py | 3 +- repo_agent/chat_with_repo/rag.py | 12 +- repo_agent/chat_with_repo/vectordb.py | 1 + repo_agent/config.py | 11 -- repo_agent/config_manager.py | 20 +- repo_agent/doc_meta_info.py | 42 ++-- repo_agent/exceptions.py | 1 + repo_agent/file_handler.py | 55 ++---- repo_agent/log.py | 16 +- repo_agent/main.py | 102 +++++++--- repo_agent/multi_task_dispatch.py | 6 +- repo_agent/project_manager.py | 28 +-- repo_agent/runner.py | 139 +++++-------- repo_agent/settings.py | 25 ++- repo_agent/utils/meta_info_utils.py | 22 +-- requirements.txt | 137 +++++++------ 28 files changed, 422 insertions(+), 541 deletions(-) delete mode 100644 config.yml.template create mode 100644 repo_agent/chat_with_repo/__init__.py delete mode 100644 repo_agent/config.py diff --git a/.gitignore b/.gitignore index 5852d0d..60d7064 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # 忽略私人配置文件,包含有api_key等信息 -config private.yml -private.yml +config private.toml +private.toml # Byte-compiled / optimized / DLL files __pycache__/ @@ -181,4 +181,5 @@ prompt_output/ log.txt .chroma_db config.yml +config.toml .pre-commit-config.yaml \ No newline at end of file diff --git a/README.md b/README.md index 141a182..425307f 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,6 @@ repo_path: /path/to/your/repo project_hierarchy: .project_hierarchy # This is a folder, where we store the project hierarchy and metainfo. This can be shared with your team members. Markdown_Docs_folder: Markdown_Docs # The folder in the root directory of your target repository to store the documentation. ignore_list: ["ignore_file1.py", "ignore_file2.py", "ignore_directory"] # Ignore some py files or folders that you don't want to generate documentation for by giving relative paths in ignore_list. -whitelist_path: /path/of/whitelist_path_json #if you provide the whitelist json with the same structure in Metainfo, RepoAgent will only process the given part. This is useful in a very big project, like "higgingface Transformers" language: en # Two-letter language codes (ISO 639-1 codes), e.g. `language: en` for English. Refer to Supported Language for more languages. max_thread_count: 10 # We support multiprocessing to speedup the process diff --git a/config.yml.template b/config.yml.template deleted file mode 100644 index 7aa81de..0000000 --- a/config.yml.template +++ /dev/null @@ -1,51 +0,0 @@ -api_keys: - gpt-3.5-turbo: - - api_key: sk-XXXX - base_url: https://example.com/v1/ - model: gpt-3.5-turbo - gpt-3.5-turbo-16k: - - api_key: sk-XXXX - base_url: https://example.com/v1/ - api_type: azure - api_version: XXX - engine: GPT-35-Turbo-16k - # you can use any kwargs supported by openai.ChatCompletion here - - api_key: sk-xxxxx - organization: org-xxxxxx - model: gpt-3.5-turbo-16k - gpt-4: - - api_key: sk-XXXX - base_url: https://example.com/v1/ - model: gpt-4 - gpt-4-32k: - - api_key: sk-XXXX - base_url: https://example.com/v1/ - api_type: XXX - api_version: XXX - engine: gpt4-32 - gpt-4-1106: - - api_key: sk-XXXX - base_url: https://example.com/v1/ - model: gpt-4-1106 - gpt-4-0125-preview: - - api_key: sk-XXXX - base_url: https://example.com/v1/ - model: gpt-4-0125-preview - -default_completion_kwargs: - model: gpt-3.5-turbo - temperature: 0.2 - request_timeout: 60 - - - -repo_path: /path/to/your/local/repo -project_hierarchy: .project_doc_record # Please NOTE that this is a folder where you can store your project hierarchy and share it with your team members. -Markdown_Docs_folder: markdown_docs # Please pay attention to the way the path is written. Do not add a slash cuz the absolute path is written starting with a slash. -ignore_list: ["ignore_file1.py", "ignore_file2.py", "ignore_directory"] # optional and if needed, relative to repo_path -whitelist_path: #if whitelist_path is not none, We only generate docs on whitelist - -language: zh -max_thread_count: 5 -max_document_tokens: 1024 # the maximum number of tokens in a document generated -log_level: info \ No newline at end of file diff --git a/pdm.lock b/pdm.lock index b3c4bd3..478302b 100644 --- a/pdm.lock +++ b/pdm.lock @@ -229,19 +229,20 @@ files = [ [[package]] name = "build" -version = "1.0.3" +version = "1.1.1" requires_python = ">= 3.7" summary = "A simple, correct Python build frontend" groups = ["default"] dependencies = [ "colorama; os_name == \"nt\"", + "importlib-metadata>=4.6; python_full_version < \"3.10.2\"", "packaging>=19.0", "pyproject-hooks", "tomli>=1.1.0; python_version < \"3.11\"", ] files = [ - {file = "build-1.0.3-py3-none-any.whl", hash = "sha256:589bf99a67df7c9cf07ec0ac0e5e2ea5d4b37ac63301c4986d1acb126aa83f8f"}, - {file = "build-1.0.3.tar.gz", hash = "sha256:538aab1b64f9828977f84bc63ae570b060a8ed1be419e7870b8b4fc5e6ea553b"}, + {file = "build-1.1.1-py3-none-any.whl", hash = "sha256:8ed0851ee76e6e38adce47e4bee3b51c771d86c64cf578d0c2245567ee200e73"}, + {file = "build-1.1.1.tar.gz", hash = "sha256:8eea65bb45b1aac2e734ba2cc8dad3a6d97d97901a395bd0ed3e7b46953d2a31"}, ] [[package]] @@ -958,7 +959,7 @@ files = [ [[package]] name = "huggingface-hub" -version = "0.21.2" +version = "0.21.3" requires_python = ">=3.8.0" summary = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" groups = ["default"] @@ -972,8 +973,8 @@ dependencies = [ "typing-extensions>=3.7.4.3", ] files = [ - {file = "huggingface_hub-0.21.2-py3-none-any.whl", hash = "sha256:16955c2b60bcff32a0778f84b9e9ae8f61d7f003da6aa1fbb7bc897a0c37b28c"}, - {file = "huggingface_hub-0.21.2.tar.gz", hash = "sha256:839f2fc69fc51797b76dcffa7edbf7fb1150176f74cb1dc2d87ca00e5e0b5611"}, + {file = "huggingface_hub-0.21.3-py3-none-any.whl", hash = "sha256:b183144336fdf2810a8c109822e0bb6ef1fd61c65da6fb60e8c3f658b7144016"}, + {file = "huggingface_hub-0.21.3.tar.gz", hash = "sha256:26a15b604e4fc7bad37c467b76456543ec849386cbca9cd7e1e135f53e500423"}, ] [[package]] @@ -1313,7 +1314,7 @@ files = [ [[package]] name = "marshmallow" -version = "3.21.0" +version = "3.21.1" requires_python = ">=3.8" summary = "A lightweight library for converting complex datatypes to and from native Python datatypes." groups = ["default"] @@ -1321,8 +1322,8 @@ dependencies = [ "packaging>=17.0", ] files = [ - {file = "marshmallow-3.21.0-py3-none-any.whl", hash = "sha256:e7997f83571c7fd476042c2c188e4ee8a78900ca5e74bd9c8097afa56624e9bd"}, - {file = "marshmallow-3.21.0.tar.gz", hash = "sha256:20f53be28c6e374a711a16165fb22a8dc6003e3f7cda1285e3ca777b9193885b"}, + {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, + {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, ] [[package]] @@ -1993,7 +1994,7 @@ files = [ [[package]] name = "posthog" -version = "3.4.2" +version = "3.5.0" summary = "Integrate PostHog into any python application." groups = ["default"] dependencies = [ @@ -2004,8 +2005,8 @@ dependencies = [ "six>=1.5", ] files = [ - {file = "posthog-3.4.2-py2.py3-none-any.whl", hash = "sha256:c7e79b2e585d16e93749874bcbcdad78d857037398ce0d8d6c474a04d0bd3bbe"}, - {file = "posthog-3.4.2.tar.gz", hash = "sha256:f0eafa663fbc4a942b49b6168a62a890635407044bbc7593051dcb9cc1208873"}, + {file = "posthog-3.5.0-py2.py3-none-any.whl", hash = "sha256:3c672be7ba6f95d555ea207d4486c171d06657eb34b3ce25eb043bfe7b6b5b76"}, + {file = "posthog-3.5.0.tar.gz", hash = "sha256:8f7e3b2c6e8714d0c0c542a2109b83a7549f63b7113a133ab2763a89245ef2ef"}, ] [[package]] @@ -2291,7 +2292,7 @@ files = [ [[package]] name = "python-dateutil" -version = "2.8.2" +version = "2.9.0.post0" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" summary = "Extensions to the standard Python datetime module" groups = ["default"] @@ -2299,8 +2300,8 @@ dependencies = [ "six>=1.5", ] files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, ] [[package]] @@ -2484,7 +2485,7 @@ files = [ [[package]] name = "rich" -version = "13.7.0" +version = "13.7.1" requires_python = ">=3.7.0" summary = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" groups = ["default"] @@ -2493,8 +2494,8 @@ dependencies = [ "pygments<3.0.0,>=2.13.0", ] files = [ - {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, - {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, ] [[package]] @@ -2595,28 +2596,28 @@ files = [ [[package]] name = "ruff" -version = "0.2.2" +version = "0.3.0" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["default", "dev"] files = [ - {file = "ruff-0.2.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0a9efb032855ffb3c21f6405751d5e147b0c6b631e3ca3f6b20f917572b97eb6"}, - {file = "ruff-0.2.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d450b7fbff85913f866a5384d8912710936e2b96da74541c82c1b458472ddb39"}, - {file = "ruff-0.2.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecd46e3106850a5c26aee114e562c329f9a1fbe9e4821b008c4404f64ff9ce73"}, - {file = "ruff-0.2.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e22676a5b875bd72acd3d11d5fa9075d3a5f53b877fe7b4793e4673499318ba"}, - {file = "ruff-0.2.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1695700d1e25a99d28f7a1636d85bafcc5030bba9d0578c0781ba1790dbcf51c"}, - {file = "ruff-0.2.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b0c232af3d0bd8f521806223723456ffebf8e323bd1e4e82b0befb20ba18388e"}, - {file = "ruff-0.2.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f63d96494eeec2fc70d909393bcd76c69f35334cdbd9e20d089fb3f0640216ca"}, - {file = "ruff-0.2.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a61ea0ff048e06de273b2e45bd72629f470f5da8f71daf09fe481278b175001"}, - {file = "ruff-0.2.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1439c8f407e4f356470e54cdecdca1bd5439a0673792dbe34a2b0a551a2fe3"}, - {file = "ruff-0.2.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:940de32dc8853eba0f67f7198b3e79bc6ba95c2edbfdfac2144c8235114d6726"}, - {file = "ruff-0.2.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0c126da55c38dd917621552ab430213bdb3273bb10ddb67bc4b761989210eb6e"}, - {file = "ruff-0.2.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3b65494f7e4bed2e74110dac1f0d17dc8e1f42faaa784e7c58a98e335ec83d7e"}, - {file = "ruff-0.2.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1ec49be4fe6ddac0503833f3ed8930528e26d1e60ad35c2446da372d16651ce9"}, - {file = "ruff-0.2.2-py3-none-win32.whl", hash = "sha256:d920499b576f6c68295bc04e7b17b6544d9d05f196bb3aac4358792ef6f34325"}, - {file = "ruff-0.2.2-py3-none-win_amd64.whl", hash = "sha256:cc9a91ae137d687f43a44c900e5d95e9617cb37d4c989e462980ba27039d239d"}, - {file = "ruff-0.2.2-py3-none-win_arm64.whl", hash = "sha256:c9d15fc41e6054bfc7200478720570078f0b41c9ae4f010bcc16bd6f4d1aacdd"}, - {file = "ruff-0.2.2.tar.gz", hash = "sha256:e62ed7f36b3068a30ba39193a14274cd706bc486fad521276458022f7bccb31d"}, + {file = "ruff-0.3.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7deb528029bacf845bdbb3dbb2927d8ef9b4356a5e731b10eef171e3f0a85944"}, + {file = "ruff-0.3.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e1e0d4381ca88fb2b73ea0766008e703f33f460295de658f5467f6f229658c19"}, + {file = "ruff-0.3.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f7dbba46e2827dfcb0f0cc55fba8e96ba7c8700e0a866eb8cef7d1d66c25dcb"}, + {file = "ruff-0.3.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:23dbb808e2f1d68eeadd5f655485e235c102ac6f12ad31505804edced2a5ae77"}, + {file = "ruff-0.3.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ef655c51f41d5fa879f98e40c90072b567c666a7114fa2d9fe004dffba00932"}, + {file = "ruff-0.3.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d0d3d7ef3d4f06433d592e5f7d813314a34601e6c5be8481cccb7fa760aa243e"}, + {file = "ruff-0.3.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b08b356d06a792e49a12074b62222f9d4ea2a11dca9da9f68163b28c71bf1dd4"}, + {file = "ruff-0.3.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9343690f95710f8cf251bee1013bf43030072b9f8d012fbed6ad702ef70d360a"}, + {file = "ruff-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1f3ed501a42f60f4dedb7805fa8d4534e78b4e196f536bac926f805f0743d49"}, + {file = "ruff-0.3.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:cc30a9053ff2f1ffb505a585797c23434d5f6c838bacfe206c0e6cf38c921a1e"}, + {file = "ruff-0.3.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5da894a29ec018a8293d3d17c797e73b374773943e8369cfc50495573d396933"}, + {file = "ruff-0.3.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:755c22536d7f1889be25f2baf6fedd019d0c51d079e8417d4441159f3bcd30c2"}, + {file = "ruff-0.3.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:dd73fe7f4c28d317855da6a7bc4aa29a1500320818dd8f27df95f70a01b8171f"}, + {file = "ruff-0.3.0-py3-none-win32.whl", hash = "sha256:19eacceb4c9406f6c41af806418a26fdb23120dfe53583df76d1401c92b7c14b"}, + {file = "ruff-0.3.0-py3-none-win_amd64.whl", hash = "sha256:128265876c1d703e5f5e5a4543bd8be47c73a9ba223fd3989d4aa87dd06f312f"}, + {file = "ruff-0.3.0-py3-none-win_arm64.whl", hash = "sha256:e3a4a6d46aef0a84b74fcd201a4401ea9a6cd85614f6a9435f2d33dd8cefbf83"}, + {file = "ruff-0.3.0.tar.gz", hash = "sha256:0886184ba2618d815067cf43e005388967b67ab9c80df52b32ec1152ab49f53a"}, ] [[package]] @@ -2687,7 +2688,7 @@ files = [ [[package]] name = "sqlalchemy" -version = "2.0.27" +version = "2.0.28" requires_python = ">=3.7" summary = "Database Abstraction Library" groups = ["default"] @@ -2696,72 +2697,72 @@ dependencies = [ "typing-extensions>=4.6.0", ] files = [ - {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d04e579e911562f1055d26dab1868d3e0bb905db3bccf664ee8ad109f035618a"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fa67d821c1fd268a5a87922ef4940442513b4e6c377553506b9db3b83beebbd8"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c7a596d0be71b7baa037f4ac10d5e057d276f65a9a611c46970f012752ebf2d"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:954d9735ee9c3fa74874c830d089a815b7b48df6f6b6e357a74130e478dbd951"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5cd20f58c29bbf2680039ff9f569fa6d21453fbd2fa84dbdb4092f006424c2e6"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:03f448ffb731b48323bda68bcc93152f751436ad6037f18a42b7e16af9e91c07"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-win32.whl", hash = "sha256:d997c5938a08b5e172c30583ba6b8aad657ed9901fc24caf3a7152eeccb2f1b4"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-win_amd64.whl", hash = "sha256:eb15ef40b833f5b2f19eeae65d65e191f039e71790dd565c2af2a3783f72262f"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c5bad7c60a392850d2f0fee8f355953abaec878c483dd7c3836e0089f046bf6"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3012ab65ea42de1be81fff5fb28d6db893ef978950afc8130ba707179b4284a"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbcd77c4d94b23e0753c5ed8deba8c69f331d4fd83f68bfc9db58bc8983f49cd"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d177b7e82f6dd5e1aebd24d9c3297c70ce09cd1d5d37b43e53f39514379c029c"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:680b9a36029b30cf063698755d277885d4a0eab70a2c7c6e71aab601323cba45"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1306102f6d9e625cebaca3d4c9c8f10588735ef877f0360b5cdb4fdfd3fd7131"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-win32.whl", hash = "sha256:5b78aa9f4f68212248aaf8943d84c0ff0f74efc65a661c2fc68b82d498311fd5"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-win_amd64.whl", hash = "sha256:15e19a84b84528f52a68143439d0c7a3a69befcd4f50b8ef9b7b69d2628ae7c4"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0de1263aac858f288a80b2071990f02082c51d88335a1db0d589237a3435fe71"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce850db091bf7d2a1f2fdb615220b968aeff3849007b1204bf6e3e50a57b3d32"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dfc936870507da96aebb43e664ae3a71a7b96278382bcfe84d277b88e379b18"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4fbe6a766301f2e8a4519f4500fe74ef0a8509a59e07a4085458f26228cd7cc"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4535c49d961fe9a77392e3a630a626af5baa967172d42732b7a43496c8b28876"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0fb3bffc0ced37e5aa4ac2416f56d6d858f46d4da70c09bb731a246e70bff4d5"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-win32.whl", hash = "sha256:7f470327d06400a0aa7926b375b8e8c3c31d335e0884f509fe272b3c700a7254"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-win_amd64.whl", hash = "sha256:f9374e270e2553653d710ece397df67db9d19c60d2647bcd35bfc616f1622dcd"}, - {file = "SQLAlchemy-2.0.27-py3-none-any.whl", hash = "sha256:1ab4e0448018d01b142c916cc7119ca573803a4745cfe341b8f95657812700ac"}, - {file = "SQLAlchemy-2.0.27.tar.gz", hash = "sha256:86a6ed69a71fe6b88bf9331594fa390a2adda4a49b5c06f98e47bf0d392534f8"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, ] [[package]] name = "sqlalchemy" -version = "2.0.27" +version = "2.0.28" extras = ["asyncio"] requires_python = ">=3.7" summary = "Database Abstraction Library" groups = ["default"] dependencies = [ - "SQLAlchemy==2.0.27", + "SQLAlchemy==2.0.28", "greenlet!=0.4.17", ] files = [ - {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d04e579e911562f1055d26dab1868d3e0bb905db3bccf664ee8ad109f035618a"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fa67d821c1fd268a5a87922ef4940442513b4e6c377553506b9db3b83beebbd8"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c7a596d0be71b7baa037f4ac10d5e057d276f65a9a611c46970f012752ebf2d"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:954d9735ee9c3fa74874c830d089a815b7b48df6f6b6e357a74130e478dbd951"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5cd20f58c29bbf2680039ff9f569fa6d21453fbd2fa84dbdb4092f006424c2e6"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:03f448ffb731b48323bda68bcc93152f751436ad6037f18a42b7e16af9e91c07"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-win32.whl", hash = "sha256:d997c5938a08b5e172c30583ba6b8aad657ed9901fc24caf3a7152eeccb2f1b4"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-win_amd64.whl", hash = "sha256:eb15ef40b833f5b2f19eeae65d65e191f039e71790dd565c2af2a3783f72262f"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c5bad7c60a392850d2f0fee8f355953abaec878c483dd7c3836e0089f046bf6"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3012ab65ea42de1be81fff5fb28d6db893ef978950afc8130ba707179b4284a"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbcd77c4d94b23e0753c5ed8deba8c69f331d4fd83f68bfc9db58bc8983f49cd"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d177b7e82f6dd5e1aebd24d9c3297c70ce09cd1d5d37b43e53f39514379c029c"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:680b9a36029b30cf063698755d277885d4a0eab70a2c7c6e71aab601323cba45"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1306102f6d9e625cebaca3d4c9c8f10588735ef877f0360b5cdb4fdfd3fd7131"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-win32.whl", hash = "sha256:5b78aa9f4f68212248aaf8943d84c0ff0f74efc65a661c2fc68b82d498311fd5"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-win_amd64.whl", hash = "sha256:15e19a84b84528f52a68143439d0c7a3a69befcd4f50b8ef9b7b69d2628ae7c4"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0de1263aac858f288a80b2071990f02082c51d88335a1db0d589237a3435fe71"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce850db091bf7d2a1f2fdb615220b968aeff3849007b1204bf6e3e50a57b3d32"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dfc936870507da96aebb43e664ae3a71a7b96278382bcfe84d277b88e379b18"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4fbe6a766301f2e8a4519f4500fe74ef0a8509a59e07a4085458f26228cd7cc"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4535c49d961fe9a77392e3a630a626af5baa967172d42732b7a43496c8b28876"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0fb3bffc0ced37e5aa4ac2416f56d6d858f46d4da70c09bb731a246e70bff4d5"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-win32.whl", hash = "sha256:7f470327d06400a0aa7926b375b8e8c3c31d335e0884f509fe272b3c700a7254"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-win_amd64.whl", hash = "sha256:f9374e270e2553653d710ece397df67db9d19c60d2647bcd35bfc616f1622dcd"}, - {file = "SQLAlchemy-2.0.27-py3-none-any.whl", hash = "sha256:1ab4e0448018d01b142c916cc7119ca573803a4745cfe341b8f95657812700ac"}, - {file = "SQLAlchemy-2.0.27.tar.gz", hash = "sha256:86a6ed69a71fe6b88bf9331594fa390a2adda4a49b5c06f98e47bf0d392534f8"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index 271c13c..67977a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ authors = [ {name = "Qinyu Luo", email = "qinyuluo123@gmail.com"}, ] maintainers = [ - {name = "Arno Edwards", email = "Arno.Edwards@outlook.com"}, + {name = "Edwards Arno", email = "Edwards.Arno@outlook.com"}, ] license = {text = "Apache-2.0"} requires-python = ">=3.10,<4.0" @@ -24,7 +24,7 @@ dependencies = [ "tomli-w>=1.0.0", ] name = "repoagent" -version = "0.0.6" +version = "0.1.0" description = "An Agent designed to offer an intelligent approach for generating project documentation." readme = "README.md" @@ -32,7 +32,7 @@ readme = "README.md" repository = "https://github.com/LOGIC-10/RepoAgent" [project.scripts] -repoagent = "repo_agent.main:app" +repoagent = "repo_agent.main:cli" [tool.pdm] [tool.pdm.dev-dependencies] diff --git a/repo_agent/__main__.py b/repo_agent/__main__.py index 9d6b066..05ccb0b 100755 --- a/repo_agent/__main__.py +++ b/repo_agent/__main__.py @@ -1,38 +1,4 @@ -import os -import sys +from .main import cli -os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) #修改当前工作目录 - -from repo_agent.runner import Runner, delete_fake_files -from repo_agent.log import logger -from repo_agent.config import CONFIG -from repo_agent.doc_meta_info import MetaInfo, DocItem -from repo_agent.utils.meta_info_utils import make_fake_files, delete_fake_files - -if len(sys.argv) == 1: - runner = Runner() - runner.run() - logger.info("文档任务完成。") -elif len(sys.argv) == 2: - if sys.argv[1] == "clean": - delete_fake_files() - elif sys.argv[1] == "print": - runner = Runner() - runner.meta_info.target_repo_hierarchical_tree.print_recursive() - elif sys.argv[1] == "diff": - runner = Runner() - if runner.meta_info.in_generation_process: # 如果不是在生成过程中,就开始检测变更 - print("this command only support pre-check") - exit() - file_path_reflections, jump_files = make_fake_files() - new_meta_info = MetaInfo.init_meta_info(file_path_reflections, jump_files) - new_meta_info.load_doc_from_older_meta(runner.meta_info) - delete_fake_files() - - ignore_list = CONFIG.get("ignore_list", []) - DocItem.check_has_task(new_meta_info.target_repo_hierarchical_tree, ignore_list) - if new_meta_info.target_repo_hierarchical_tree.has_task: - print("the following docs will be generated/updated:") - new_meta_info.target_repo_hierarchical_tree.print_recursive(diff_status = True, ignore_list = ignore_list) - else: - print("no docs will be generated/updated, check your source-code update") \ No newline at end of file +if __name__ == "__main__": + cli() \ No newline at end of file diff --git a/repo_agent/change_detector.py b/repo_agent/change_detector.py index 5b931fe..23fc092 100644 --- a/repo_agent/change_detector.py +++ b/repo_agent/change_detector.py @@ -1,10 +1,12 @@ -import git -import re, os +import os +import re import subprocess + +import git from colorama import Fore, Style -from repo_agent.config import CONFIG from repo_agent.file_handler import FileHandler +from repo_agent.settings import setting class ChangeDetector: @@ -166,7 +168,7 @@ def get_to_be_staged_files(self): print(f"{Fore.LIGHTYELLOW_EX}target_repo_path{Style.RESET_ALL}: {self.repo_path}") print(f"{Fore.LIGHTMAGENTA_EX}already_staged_files{Style.RESET_ALL}:{staged_files}") - project_hierarchy = CONFIG["project_hierarchy"] + project_hierarchy = setting.project.hierarchy_name # diffs是所有未暂存更改文件的列表。这些更改文件是相对于工作区(working directory)的,也就是说,它们是自上次提交(commit)以来在工作区发生的更改,但还没有被添加到暂存区(staging area) # 比如原本存在的md文件现在由于代码的变更发生了更新,就会标记为未暂存diff diffs = self.repo.index.diff(None) @@ -178,7 +180,7 @@ def get_to_be_staged_files(self): # 处理untrack_files中的内容 for untracked_file in untracked_files: # 连接repo_path和untracked_file以获取完整的绝对路径 - if untracked_file.startswith(CONFIG["Markdown_Docs_folder"]): + if untracked_file.startswith(setting.project.markdown_docs_name): to_be_staged_files.append(untracked_file) continue print(f"rel_untracked_file:{rel_untracked_file}") @@ -187,7 +189,7 @@ def get_to_be_staged_files(self): if rel_untracked_file.endswith(".md"): # 把rel_untracked_file从CONFIG['Markdown_Docs_folder']中拆离出来。判断是否能跟暂存区中的某一个.py文件对应上 rel_untracked_file = os.path.relpath( - rel_untracked_file, CONFIG["Markdown_Docs_folder"] + rel_untracked_file, setting.project.markdown_docs_name ) corresponding_py_file = os.path.splitext(rel_untracked_file)[0] + ".py" print( @@ -198,7 +200,7 @@ def get_to_be_staged_files(self): to_be_staged_files.append( os.path.join( self.repo_path.lstrip("/"), - CONFIG["Markdown_Docs_folder"], + setting.project.markdown_docs_name, rel_untracked_file, ) ) @@ -211,7 +213,7 @@ def get_to_be_staged_files(self): for unstaged_file in unstaged_files: # 连接repo_path和unstaged_file以获取完整的绝对路径 - if unstaged_file.startswith(CONFIG["Markdown_Docs_folder"]): + if unstaged_file.startswith(setting.project.markdown_docs_name): # abs_unstaged_file = os.path.join(self.repo_path, unstaged_file) # # # 获取相对于仓库根目录的相对路径 # # rel_unstaged_file = os.path.relpath(abs_unstaged_file, self.repo_path) @@ -227,7 +229,7 @@ def get_to_be_staged_files(self): if unstaged_file.endswith(".md"): # 把rel_unstaged_file从CONFIG['Markdown_Docs_folder']中拆离出来。判断是否能跟暂存区中的某一个.py文件对应上 rel_unstaged_file = os.path.relpath( - rel_unstaged_file, CONFIG["Markdown_Docs_folder"] + rel_unstaged_file, setting.project.markdown_docs_name ) corresponding_py_file = os.path.splitext(rel_unstaged_file)[0] + ".py" print(f"corresponding_py_file:{corresponding_py_file}") @@ -236,7 +238,7 @@ def get_to_be_staged_files(self): to_be_staged_files.append( os.path.join( self.repo_path.lstrip("/"), - CONFIG["Markdown_Docs_folder"], + setting.project.markdown_docs_name, rel_unstaged_file, ) ) diff --git a/repo_agent/chat_engine.py b/repo_agent/chat_engine.py index 9567441..2586f05 100644 --- a/repo_agent/chat_engine.py +++ b/repo_agent/chat_engine.py @@ -1,14 +1,17 @@ +import inspect +import os import sys -import tiktoken import time -import inspect -from repo_agent.log import logger -from repo_agent.settings import setting, max_input_tokens_map -from repo_agent.prompt import SYS_PROMPT, USR_PROMPT +from dataclasses import dataclass + +import tiktoken +from openai import APIConnectionError, OpenAI + from repo_agent.doc_meta_info import DocItem from repo_agent.log import logger -from openai import OpenAI, APIConnectionError -from dataclasses import dataclass +from repo_agent.prompt import SYS_PROMPT, USR_PROMPT +from repo_agent.settings import max_input_tokens_map, setting + def get_import_statements(): source_lines = inspect.getsourcelines(sys.modules[__name__])[0] @@ -255,7 +258,7 @@ def get_relationship_description(referencer_content, reference_letter): for k, v in max_input_tokens_map.items() if (v - max_tokens) > total_tokens } # 抽取出所有上下文长度大于当前总输入tokens的模型 - for model_name, max_input_length in larger_models: + for model_name, max_input_length in larger_models.items(): if max_input_length - max_tokens > total_tokens: try: # Attempt to make a request with the larger model diff --git a/repo_agent/chat_with_repo/__init__.py b/repo_agent/chat_with_repo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/repo_agent/chat_with_repo/gradio_interface.py b/repo_agent/chat_with_repo/gradio_interface.py index 7df59db..634ec21 100644 --- a/repo_agent/chat_with_repo/gradio_interface.py +++ b/repo_agent/chat_with_repo/gradio_interface.py @@ -1,5 +1,6 @@ import gradio as gr import markdown + from repo_agent.log import logger diff --git a/repo_agent/chat_with_repo/json_handler.py b/repo_agent/chat_with_repo/json_handler.py index cfe97aa..a06114e 100644 --- a/repo_agent/chat_with_repo/json_handler.py +++ b/repo_agent/chat_with_repo/json_handler.py @@ -1,5 +1,6 @@ import json import sys + from repo_agent.log import logger @@ -87,4 +88,4 @@ def search_code_contents_by_name(self, file_path, search_text): if __name__ == "__main__": processor = JsonFileProcessor("database.json") - md_contents = processor.extract_md_contents() + md_contents,extracted_contents = processor.extract_data() diff --git a/repo_agent/chat_with_repo/main.py b/repo_agent/chat_with_repo/main.py index abdfb7b..35a831b 100644 --- a/repo_agent/chat_with_repo/main.py +++ b/repo_agent/chat_with_repo/main.py @@ -1,16 +1,12 @@ -import os from repo_agent.chat_with_repo.gradio_interface import GradioInterface from repo_agent.chat_with_repo.rag import RepoAssistant -from repo_agent.config import CONFIG +from repo_agent.settings import setting def main(): - _model = CONFIG["default_completion_kwargs"]["model"] - api_key = CONFIG["api_keys"][_model][0]["api_key"] - api_base = CONFIG["api_keys"][_model][0]["base_url"] - db_path = os.path.join( - CONFIG["repo_path"], CONFIG["project_hierarchy"], "project_hierarchy.json" - ) + api_key = setting.chat_completion.openai_api_key + api_base = setting.chat_completion.base_url + db_path = setting.project.target_repo / setting.project.hierarchy_name / "project_hierarchy.json" assistant = RepoAssistant(api_key, api_base, db_path) md_contents,meta_data = assistant.json_data.extract_data() diff --git a/repo_agent/chat_with_repo/prompt.py b/repo_agent/chat_with_repo/prompt.py index f7ba781..56cb576 100644 --- a/repo_agent/chat_with_repo/prompt.py +++ b/repo_agent/chat_with_repo/prompt.py @@ -1,6 +1,7 @@ from llama_index.llms import OpenAI -from repo_agent.log import logger + from repo_agent.chat_with_repo.json_handler import JsonFileProcessor +from repo_agent.log import logger class TextAnalysisTool: diff --git a/repo_agent/chat_with_repo/rag.py b/repo_agent/chat_with_repo/rag.py index 420a1a7..cbd9f1d 100644 --- a/repo_agent/chat_with_repo/rag.py +++ b/repo_agent/chat_with_repo/rag.py @@ -1,12 +1,14 @@ -from repo_agent.chat_with_repo.json_handler import JsonFileProcessor -from repo_agent.chat_with_repo.vectordb import ChromaManager -from repo_agent.chat_with_repo.prompt import TextAnalysisTool -from repo_agent.log import logger +import json + from llama_index import PromptTemplate from llama_index.llms import OpenAI -import json from openai import OpenAI as AI +from repo_agent.chat_with_repo.json_handler import JsonFileProcessor +from repo_agent.chat_with_repo.prompt import TextAnalysisTool +from repo_agent.chat_with_repo.vectordb import ChromaManager +from repo_agent.log import logger + class RepoAssistant: def __init__(self, api_key, api_base, db_path): diff --git a/repo_agent/chat_with_repo/vectordb.py b/repo_agent/chat_with_repo/vectordb.py index 5e142db..fbb543f 100644 --- a/repo_agent/chat_with_repo/vectordb.py +++ b/repo_agent/chat_with_repo/vectordb.py @@ -1,5 +1,6 @@ import chromadb from chromadb.utils import embedding_functions + from repo_agent.log import logger diff --git a/repo_agent/config.py b/repo_agent/config.py deleted file mode 100644 index d7d461d..0000000 --- a/repo_agent/config.py +++ /dev/null @@ -1,11 +0,0 @@ -import yaml -import sys - -try: - CONFIG = yaml.load(open("config.yml", "r"), Loader=yaml.FullLoader) -except FileNotFoundError: - print( - "The file does not exist! Maybe you forgot to rename config.yml.template to config.yml and update the essential content" - ) - sys.exit(1) # Exits the program - diff --git a/repo_agent/config_manager.py b/repo_agent/config_manager.py index cceef76..1c07349 100644 --- a/repo_agent/config_manager.py +++ b/repo_agent/config_manager.py @@ -1,19 +1,19 @@ # repo_agent/config_manager.py import os -import tomli, tomli_w from pathlib import Path -from loguru import logger from typing import Dict +import tomli +import tomli_w + + def get_config_path() -> Path: # 首先检查当前工作目录的父目录 - parent_directory = Path.cwd().parent + parent_directory = Path.cwd() local_config_path = parent_directory / 'config.toml' - logger.debug(f'Checking local config path: {local_config_path}') # 如果在程序目录找到了 config.toml,则直接返回这个路径 if local_config_path.exists(): - logger.debug(f'Found local config at: {local_config_path}') return local_config_path # 如果在父目录没有找到 config.toml,按照原来的逻辑进行 @@ -23,7 +23,7 @@ def get_config_path() -> Path: home_config_path = Path.home() / '.repo_agent' elif os_name == 'nt': # 对于 Windows,使用 APPDATA 目录 - home_config_path = Path(os.getenv('APPDATA')) / 'repo_agent' + home_config_path = Path(os.getenv('APPDATA')) / 'repo_agent' # type: ignore else: # 如果操作系统检测失败,默认使用一个本地目录 home_config_path = Path.cwd() / 'repo_agent' @@ -31,24 +31,20 @@ def get_config_path() -> Path: # 确保配置目录存在 home_config_path.mkdir(parents=True, exist_ok=True) config_path = home_config_path / 'config.toml' - logger.debug(f'Using home config path: {config_path}') # 确保配置文件存在,如果不存在则创建一个空文件 if not config_path.exists(): - logger.debug(f'Config file does not exist, creating: {config_path}') config_path.touch() # 返回包含文件名的完整路径 return config_path -def read_config(file_path: Path | None = None) -> Dict[str, any]: +def read_config(file_path: Path | None = None) -> Dict[str, any]: # type: ignore if file_path is None: file_path = get_config_path() - logger.debug(f"Reading configuration from {file_path}") - with open(file_path, "rb") as f: try: toml_dict = tomli.load(f) @@ -77,8 +73,6 @@ def write_config(update_config: dict, file_path: Path | None = None) -> None: with open(file_path, "wb") as f: tomli_w.dump(existing_config, f) - logger.success("Configuration updated successfully.") - if __name__ == '__main__': # Sample TOML data to be written to the configuration file sample_toml_data = """\ diff --git a/repo_agent/doc_meta_info.py b/repo_agent/doc_meta_info.py index de9ca48..996d22f 100644 --- a/repo_agent/doc_meta_info.py +++ b/repo_agent/doc_meta_info.py @@ -1,22 +1,26 @@ """存储doc对应的信息,同时处理引用的关系 """ from __future__ import annotations + +import json +import os import threading from dataclasses import dataclass, field -from typing import List, Dict, Any, Callable, Optional +from enum import Enum, auto, unique +from pathlib import Path +from typing import Any, Callable, Dict, List, Optional + +import jedi from colorama import Fore, Style -from enum import Enum, unique, auto from prettytable import PrettyTable -import os -import json -import jedi from tqdm import tqdm +from repo_agent.file_handler import FileHandler from repo_agent.log import logger +from repo_agent.multi_task_dispatch import Task, TaskManager +from repo_agent.settings import setting from repo_agent.utils.meta_info_utils import latest_verison_substring -from repo_agent.config import CONFIG -from repo_agent.file_handler import FileHandler -from repo_agent.multi_task_dispatch import TaskManager, Task + @unique class EdgeType(Enum): @@ -61,9 +65,9 @@ def print_self(self): color = Fore.BLUE return color + self.name + Style.RESET_ALL - def get_edge_type( + def get_edge_type(self, from_item_type: DocItemType, to_item_type: DocItemType - ) -> EdgeType: + ): pass @@ -76,7 +80,7 @@ class DocItemStatus(Enum): referencer_not_exist = auto() # 曾经引用他的obj被删除了,或者不再引用他了 -def need_to_generate(doc_item: DocItem, ignore_list: List) -> bool: +def need_to_generate(doc_item: DocItem, ignore_list: List = []) -> bool: """只生成item的,文件及更高粒度都跳过。另外如果属于一个blacklist的文件也跳过""" if doc_item.item_status == DocItemStatus.doc_up_to_date: return False @@ -232,7 +236,7 @@ def find(self, recursive_file_path: list) -> Optional[DocItem]: return now @staticmethod - def check_has_task(now_item: DocItem, ignore_list) -> bool: + def check_has_task(now_item: DocItem, ignore_list: List = []): if need_to_generate(now_item, ignore_list=ignore_list): now_item.has_task = True for _, child in now_item.children.items(): @@ -248,7 +252,7 @@ def print_indent(indent=0): return " " * indent + "|-" print_obj_name = self.obj_name if self.item_type == DocItemType._repo: - print_obj_name = CONFIG["repo_path"] + print_obj_name = setting.project.target_repo if diff_status and need_to_generate(self, ignore_list=ignore_list): print( print_indent(indent) + f"{self.item_type.print_self()}: {print_obj_name} : {self.item_status.name}", @@ -315,7 +319,7 @@ class MetaInfo: @staticmethod def init_meta_info(file_path_reflections, jump_files) -> MetaInfo: """从一个仓库path中初始化metainfo""" - project_abs_path = CONFIG["repo_path"] + project_abs_path = setting.project.target_repo print(f"{Fore.LIGHTRED_EX}Initializing MetaInfo: {Style.RESET_ALL}from {project_abs_path}") file_handler = FileHandler(project_abs_path, None) repo_structure = file_handler.generate_overall_structure(file_path_reflections, jump_files) @@ -326,7 +330,7 @@ def init_meta_info(file_path_reflections, jump_files) -> MetaInfo: return metainfo @staticmethod - def from_checkpoint_path(checkpoint_dir_path: str) -> MetaInfo: + def from_checkpoint_path(checkpoint_dir_path: str | Path ) -> MetaInfo: """从已有的metainfo dir里面读取metainfo""" project_hierarchy_json_path = os.path.join( checkpoint_dir_path, "project_hierarchy.json" @@ -340,7 +344,7 @@ def from_checkpoint_path(checkpoint_dir_path: str) -> MetaInfo: os.path.join(checkpoint_dir_path, "meta-info.json"), "r", encoding="utf-8" ) as reader: meta_data = json.load(reader) - metainfo.repo_path = CONFIG["repo_path"] + metainfo.repo_path = str(setting.project.target_repo) # Convert DirectoryPath to string metainfo.document_version = meta_data["doc_version"] metainfo.fake_file_reflection = meta_data["fake_file_reflection"] metainfo.jump_files = meta_data["jump_files"] @@ -352,7 +356,7 @@ def from_checkpoint_path(checkpoint_dir_path: str) -> MetaInfo: ) return metainfo - def checkpoint(self, target_dir_path: str, flash_reference_relation=False): + def checkpoint(self, target_dir_path: str | Path, flash_reference_relation=False): """ Save the MetaInfo object to the specified directory. @@ -781,10 +785,10 @@ def from_project_hierarchy_json(project_hierarchy_json) -> MetaInfo: for file_name, file_content in tqdm(project_hierarchy_json.items(),desc="parsing parent relationship"): # 首先parse file archi - if not os.path.exists(os.path.join(CONFIG["repo_path"], file_name)): + if not os.path.exists(os.path.join(setting.project.target_repo, file_name)): logger.info(f"deleted content: {file_name}") continue - elif os.path.getsize(os.path.join(CONFIG["repo_path"], file_name)) == 0: + elif os.path.getsize(os.path.join(setting.project.target_repo, file_name)) == 0: logger.info(f"blank content: {file_name}") continue diff --git a/repo_agent/exceptions.py b/repo_agent/exceptions.py index fb3ca41..73e58a8 100644 --- a/repo_agent/exceptions.py +++ b/repo_agent/exceptions.py @@ -1,4 +1,5 @@ from openai import APIConnectionError + from repo_agent.log import logger diff --git a/repo_agent/file_handler.py b/repo_agent/file_handler.py index b648df4..95f3764 100644 --- a/repo_agent/file_handler.py +++ b/repo_agent/file_handler.py @@ -1,13 +1,16 @@ # FileHandler 类,实现对文件的读写操作,这里的文件包括markdown文件和python文件 # repo_agent/file_handler.py -import git -import os, json import ast -from tqdm import tqdm +import json +import os + +import git from colorama import Fore, Style -from repo_agent.utils.meta_info_utils import latest_verison_substring -from repo_agent.config import CONFIG +from tqdm import tqdm + +from repo_agent.settings import setting from repo_agent.utils.gitignore_checker import GitignoreChecker +from repo_agent.utils.meta_info_utils import latest_verison_substring class FileHandler: @@ -17,9 +20,7 @@ class FileHandler: def __init__(self, repo_path, file_path): self.file_path = file_path # 这里的file_path是相对于仓库根目录的路径 self.repo_path = repo_path - self.project_hierarchy = os.path.join( - repo_path, CONFIG["project_hierarchy"], "project_hierarchy.json" - ) + self.project_hierarchy = setting.project.target_repo / setting.project.hierarchy_name def read_file(self): """ @@ -264,7 +265,7 @@ def generate_overall_structure(self, file_path_reflections, jump_files) -> dict: # elif not_ignored_files.endswith(latest_version): # """如果某文件被删除但没有暂存,文件系统有fake_file但没有对应的原始文件""" # for k,v in file_path_reflections.items(): - # if v == not_ignored_files and not os.path.exists(os.path.join(CONFIG["repo_path"], not_ignored_files)): + # if v == not_ignored_files and not os.path.exists(os.path.join(setting.project.target_repo, not_ignored_files)): # print(f"{Fore.LIGHTYELLOW_EX}[Unstaged DeleteFile] load fake-file-content: {Style.RESET_ALL}{k}") # normal_file_names = k #原来的名字 # break @@ -344,46 +345,12 @@ def convert_to_markdown_file(self, file_path=None): return markdown - # def convert_all_to_markdown_files_from_json(self): - # """ - # Converts all files to markdown format based on the JSON data. - - # Reads the project hierarchy from a JSON file, checks if the Markdown_docs folder exists, - # creates it if it doesn't, and then iterates through each file in the JSON data. - # For each file, it converts the file to markdown format and writes it to the Markdown_docs folder. - - # Args: - # self (object): The file_handler object. - - # Returns: - # None - # """ - # with open(self.project_hierarchy, 'r', encoding='utf-8') as f: - # json_data = json.load(f) - - # # 检查根目录是否存在Markdown_docs文件夹,如果不存在则创建 - # markdown_docs_path = os.path.join(self.repo_path, CONFIG['Markdown_Docs_folder']) - # if not os.path.exists(markdown_docs_path): - # os.mkdir(markdown_docs_path) - - # # 遍历json_data["files"]列表中的每个字典 - # for rel_file_path, file_dict in json_data.items(): - # md_path = os.path.join(markdown_docs_path, rel_file_path.replace('.py', '.md')) - # markdown = self.convert_to_markdown_file(rel_file_path) - - # # 检查目录是否存在,如果不存在,就创建它 - # os.makedirs(os.path.dirname(md_path), exist_ok=True) - - # # 将markdown文档写入到Markdown_docs文件夹中 - # with open(md_path, 'w', encoding='utf-8') as f: - # f.write(markdown) - if __name__ == "__main__": # 打开py文件读取源代码 # file_handler = FileHandler('/path/to/repo', '/path/to/file.py') - file_handler = FileHandler(CONFIG["repo_path"], "XAgent/engines/pipeline_old.py") + file_handler = FileHandler(setting.project.target_repo, "XAgent/engines/pipeline_old.py") # file_handler.generate_markdown_from_json() file_handler.convert_all_to_markdown_files_from_json() # code_content = file_handler.read_file() diff --git a/repo_agent/log.py b/repo_agent/log.py index 45935f6..695a1f4 100644 --- a/repo_agent/log.py +++ b/repo_agent/log.py @@ -1,16 +1,8 @@ # repo_agent/log.py -import sys import logging -from loguru import logger -from enum import StrEnum +import sys -class LogLevel(StrEnum): - DEBUG = 'DEBUG' - INFO = 'INFO' - WARNING = 'WARNING' - ERROR = 'ERROR' - CRITICAL = 'CRITICAL' -# from repo_agent.config import CONFIG +from loguru import logger logger = logger.opt(colors=True) """ @@ -61,8 +53,8 @@ def emit(self, record): # Find caller from where the logged message originated frame, depth = logging.currentframe(), 2 - while frame.f_code.co_filename == logging.__file__: - frame = frame.f_back + while frame.f_code.co_filename == logging.__file__: # type: ignore + frame = frame.f_back # type: ignore depth += 1 # Log to Loguru diff --git a/repo_agent/main.py b/repo_agent/main.py index 9d9ff86..6ae2bc5 100644 --- a/repo_agent/main.py +++ b/repo_agent/main.py @@ -1,20 +1,26 @@ -import click -from repo_agent.settings import ( - Setting, - ProjectSettings, - ChatCompletionSettings, -) -from repo_agent.config_manager import write_config from importlib import metadata + +import click +from iso639 import Language, LanguageNotFoundError from loguru import logger from tenacity import ( retry, - stop_after_attempt, retry_if_exception_type, stop_after_attempt, ) -from iso639 import Language, LanguageNotFoundError -from repo_agent.log import LogLevel + +from repo_agent.config_manager import write_config +from repo_agent.doc_meta_info import DocItem, MetaInfo +from repo_agent.log import logger, set_logger_level_from_config +from repo_agent.runner import Runner, delete_fake_files +from repo_agent.settings import ( + ChatCompletionSettings, + LogLevel, + ProjectSettings, + Setting, + setting, +) +from repo_agent.utils.meta_info_utils import delete_fake_files, make_fake_files # 尝试获取版本号,如果失败,则使用默认版本号。 try: @@ -73,7 +79,9 @@ def configure(): ), ignore_list=click.prompt( "Enter files or directories to ignore, separated by commas", - default=",".join(project_settings_default_instance.ignore_list), + default=",".join( + str(path) for path in project_settings_default_instance.ignore_list + ), ).split(","), language=language_prompt( default_language=project_settings_default_instance.language @@ -97,7 +105,7 @@ def configure(): ), ) - logger.info("Project settings saved successfully.") + logger.success("Project settings saved successfully.") chat_completion_instance = ChatCompletionSettings( model=click.prompt( @@ -117,14 +125,12 @@ def configure(): "Enter the base URL", default=str(chat_completion_default_instance.base_url) ), ) - logger.info("Chat completion settings saved successfully.") + logger.success("Chat completion settings saved successfully.") update_setting = Setting( project=project_settings_instance, chat_completion=chat_completion_instance ) - logger.debug(f"Current settings: {update_setting.model_dump()}") - write_config(update_setting.model_dump()) @@ -132,7 +138,7 @@ def configure(): @click.option( "--model", "-m", - default=chat_completion_default_instance.model, + default=setting.chat_completion.model, show_default=True, help="Specifies the model to use for completion.", type=str, @@ -140,7 +146,7 @@ def configure(): @click.option( "--temperature", "-t", - default=chat_completion_default_instance.temperature, + default=setting.chat_completion.temperature, show_default=True, help="Sets the generation temperature for the model. Lower values make the model more deterministic.", type=float, @@ -148,7 +154,7 @@ def configure(): @click.option( "--request-timeout", "-r", - default=chat_completion_default_instance.request_timeout, + default=setting.chat_completion.request_timeout, show_default=True, help="Defines the timeout in seconds for the API request.", type=int, @@ -156,7 +162,7 @@ def configure(): @click.option( "--base-url", "-b", - default=chat_completion_default_instance.base_url, + default=setting.chat_completion.base_url, show_default=True, help="The base URL for the API calls.", type=str, @@ -164,7 +170,7 @@ def configure(): @click.option( "--target-repo-path", "-tp", - default=project_settings_default_instance.target_repo, + default=setting.project.target_repo, show_default=True, help="The file system path to the target repository. This path is used as the root for documentation generation.", type=click.Path(), @@ -172,7 +178,7 @@ def configure(): @click.option( "--hierarchy-path", "-hp", - default=project_settings_default_instance.hierarchy_name, + default=setting.project.hierarchy_name, show_default=True, help="The name or path for the project hierarchy file, used to organize documentation structure.", type=str, @@ -180,7 +186,7 @@ def configure(): @click.option( "--markdown-docs-path", "-mdp", - default=project_settings_default_instance.markdown_docs_name, + default=setting.project.markdown_docs_name, show_default=True, help="The folder path where Markdown documentation will be stored or generated.", type=str, @@ -188,7 +194,7 @@ def configure(): @click.option( "--ignore-list", "-i", - default=project_settings_default_instance.ignore_list, + default=setting.project.ignore_list, show_default=True, help="A list of files or directories to ignore during documentation generation, separated by commas.", multiple=True, @@ -197,7 +203,7 @@ def configure(): @click.option( "--language", "-l", - default=project_settings_default_instance.language, + default=setting.project.language, show_default=True, help="The ISO 639 code or language name for the documentation. ", type=str, @@ -205,7 +211,7 @@ def configure(): @click.option( "--log-level", "-ll", - default=project_settings_default_instance.log_level, + default=setting.project.log_level, show_default=True, help="Sets the logging level (e.g., DEBUG, INFO, WARNING, ERROR, CRITICAL) for the application. Default is INFO.", type=click.Choice([level.value for level in LogLevel], case_sensitive=False), @@ -244,6 +250,52 @@ def run( project=project_settings, chat_completion=chat_completion_settings ) write_config(settings.model_dump()) + set_logger_level_from_config(log_level=setting.project.log_level) + + runner = Runner() + runner.run() + logger.success("Documentation task completed.") + + +@cli.command() +def clean(): + """Clean the fake files generated by the documentation process.""" + delete_fake_files() + logger.success("Fake files have been cleaned up.") + + +@cli.command() +def print_hierarchy(): + """Print the hierarchy of the target repository.""" + runner = Runner() + runner.meta_info.target_repo_hierarchical_tree.print_recursive() + logger.success("Hierarchy printed.") + + +@cli.command() +def diff(): + """Check for changes and print which documents will be updated or generated.""" + runner = Runner() + if runner.meta_info.in_generation_process: # 如果不是在生成过程中,就开始检测变更 + click.echo("This command only supports pre-check") + raise click.Abort() + + file_path_reflections, jump_files = make_fake_files() + new_meta_info = MetaInfo.init_meta_info(file_path_reflections, jump_files) + new_meta_info.load_doc_from_older_meta(runner.meta_info) + delete_fake_files() + + DocItem.check_has_task( + new_meta_info.target_repo_hierarchical_tree, + ignore_list=setting.project.ignore_list, + ) + if new_meta_info.target_repo_hierarchical_tree.has_task: + click.echo("The following docs will be generated/updated:") + new_meta_info.target_repo_hierarchical_tree.print_recursive( + diff_status=True, ignore_list=setting.project.ignore_list + ) + else: + click.echo("No docs will be generated/updated, check your source-code update") if __name__ == "__main__": diff --git a/repo_agent/multi_task_dispatch.py b/repo_agent/multi_task_dispatch.py index 1c22e43..315f42b 100644 --- a/repo_agent/multi_task_dispatch.py +++ b/repo_agent/multi_task_dispatch.py @@ -1,8 +1,10 @@ from __future__ import annotations + +import random import threading import time -import random -from typing import List, Callable, Dict, Any +from typing import Any, Callable, Dict, List + from colorama import Fore, Style diff --git a/repo_agent/project_manager.py b/repo_agent/project_manager.py index 00a4439..bbc0b07 100644 --- a/repo_agent/project_manager.py +++ b/repo_agent/project_manager.py @@ -1,4 +1,5 @@ import os + import jedi @@ -66,33 +67,6 @@ def tree_to_string(tree, indent=0): return tree_to_string(path_tree) - # def find_all_referencer(self, variable_name, file_path, line_number, column_number): - # """ - # Find all references of a variable in a given file. - - # Args: - # variable_name (str): The name of the variable to search for. - # file_path (str): The path of the file to search in. - # line_number (int): The line number where the variable is located. - # column_number (int): The column number where the variable is located. - - # Returns: - # list: A list of tuples containing the file path, line number, and column number of each reference. - - # """ - # script = jedi.Script(path=os.path.join(self.repo_path, file_path)) - # references = script.get_references(line=line_number, column=column_number) - - # try: - # # Filter out references with variable_name and return their positions - # variable_references = [ref for ref in references if ref.name == variable_name] - # return [(os.path.relpath(ref.module_path, self.repo_path), ref.line, ref.column) for ref in variable_references if not (ref.line == line_number and ref.column == column_number)] - # except Exception as e: - # # Print error message and related parameters - # print(f"Error occurred: {e}") - # print(f"Parameters: variable_name={variable_name}, file_path={file_path}, line_number={line_number}, column_number={column_number}") - # return [] - if __name__ == "__main__": project_manager = ProjectManager(repo_path="", project_hierarchy="") diff --git a/repo_agent/runner.py b/repo_agent/runner.py index 5ac5835..612c8f5 100644 --- a/repo_agent/runner.py +++ b/repo_agent/runner.py @@ -1,69 +1,51 @@ -import threading -import os, json import json -from tqdm import tqdm -from typing import List -from functools import partial +import os +import shutil import subprocess -import shutil +import threading +import time from concurrent.futures import ThreadPoolExecutor +from functools import partial + from colorama import Fore, Style -import time +from tqdm import tqdm -from repo_agent.file_handler import FileHandler -from repo_agent.utils.meta_info_utils import make_fake_files, delete_fake_files from repo_agent.change_detector import ChangeDetector -from repo_agent.project_manager import ProjectManager from repo_agent.chat_engine import ChatEngine -from repo_agent.doc_meta_info import MetaInfo, DocItem, DocItemStatus, need_to_generate +from repo_agent.doc_meta_info import DocItem, DocItemStatus, MetaInfo, need_to_generate +from repo_agent.file_handler import FileHandler from repo_agent.log import logger -from repo_agent.config import CONFIG from repo_agent.multi_task_dispatch import worker - - - - -def load_whitelist(): - if CONFIG["whitelist_path"] != None: - assert os.path.exists( - CONFIG["whitelist_path"] - ), f"whitelist_path must be a json-file,and must exists: {CONFIG['whitelist_path']}" - with open(CONFIG["whitelist_path"], "r") as reader: - white_list_json_data = json.load(reader) - - return white_list_json_data - else: - return None +from repo_agent.project_manager import ProjectManager +from repo_agent.settings import setting +from repo_agent.utils.meta_info_utils import delete_fake_files, make_fake_files class Runner: def __init__(self): + self.absolute_project_hierarchy_path = setting.project.target_repo / setting.project.hierarchy_name + self.project_manager = ProjectManager( - repo_path=CONFIG["repo_path"], project_hierarchy=CONFIG["project_hierarchy"] + repo_path=setting.project.target_repo, + project_hierarchy=setting.project.hierarchy_name ) - self.change_detector = ChangeDetector(repo_path=CONFIG["repo_path"]) - self.chat_engine = ChatEngine(CONFIG=CONFIG) + self.change_detector = ChangeDetector(repo_path=setting.project.target_repo) + self.chat_engine = ChatEngine(project_manager=self.project_manager) - if not os.path.exists( - os.path.join(CONFIG["repo_path"], CONFIG["project_hierarchy"]) - ): + + if not self.absolute_project_hierarchy_path.exists(): file_path_reflections, jump_files = make_fake_files() self.meta_info = MetaInfo.init_meta_info(file_path_reflections, jump_files) self.meta_info.checkpoint( - target_dir_path=os.path.join( - CONFIG["repo_path"], CONFIG["project_hierarchy"] - ) + target_dir_path=self.absolute_project_hierarchy_path ) else: # 如果存在全局结构信息文件夹.project_hierarchy,就从中加载 self.meta_info = MetaInfo.from_checkpoint_path( - os.path.join(CONFIG["repo_path"], CONFIG["project_hierarchy"]) + self.absolute_project_hierarchy_path ) - self.meta_info.white_list = load_whitelist() self.meta_info.checkpoint( # 更新白名单后也要重新将全局信息写入到.project_doc_record文件夹中 - target_dir_path=os.path.join( - CONFIG["repo_path"], CONFIG["project_hierarchy"] - ) + target_dir_path=self.absolute_project_hierarchy_path ) self.runner_lock = threading.Lock() @@ -88,30 +70,22 @@ def get_all_pys(self, directory): def generate_doc_for_a_single_item(self, doc_item: DocItem): """为一个对象生成文档""" - try: - rel_file_path = doc_item.get_full_name() + rel_file_path = doc_item.get_full_name() - ignore_list = CONFIG.get("ignore_list", []) - if not need_to_generate(doc_item, ignore_list): - print(f"内容被忽略/文档已生成,跳过:{doc_item.get_full_name()}") - else: - print(f" -- 正在生成文档 {Fore.LIGHTYELLOW_EX}{doc_item.item_type.name}: {doc_item.get_full_name()}{Style.RESET_ALL}") - file_handler = FileHandler(CONFIG["repo_path"], rel_file_path) - response_message = self.chat_engine.generate_doc( - doc_item=doc_item, - file_handler=file_handler, - ) - doc_item.md_content.append(response_message.content) - doc_item.item_status = DocItemStatus.doc_up_to_date - self.meta_info.checkpoint( - target_dir_path=os.path.join( - CONFIG["repo_path"], CONFIG["project_hierarchy"] - ) - ) - except Exception as e: - logger.info(f" 多次尝试后生成文档失败,跳过:{doc_item.get_full_name()}") - logger.info("Error:", e) - doc_item.item_status = DocItemStatus.doc_has_not_been_generated + if not need_to_generate(doc_item, setting.project.ignore_list): + print(f"内容被忽略/文档已生成,跳过:{doc_item.get_full_name()}") + else: + print(f" -- 正在生成文档 {Fore.LIGHTYELLOW_EX}{doc_item.item_type.name}: {doc_item.get_full_name()}{Style.RESET_ALL}") + file_handler = FileHandler(setting.project.target_repo, rel_file_path) + response_message = self.chat_engine.generate_doc( + doc_item=doc_item, + file_handler=file_handler, + ) + doc_item.md_content.append(response_message.content) + doc_item.item_status = DocItemStatus.doc_up_to_date + self.meta_info.checkpoint( + target_dir_path=self.absolute_project_hierarchy_path + ) def first_generate(self): """ @@ -121,8 +95,7 @@ def first_generate(self): **注意**:这个生成first_generate的过程中,目标仓库代码不能修改。也就是说,一个document的生成过程必须绑定代码为一个版本。 """ logger.info("Starting to generate documentation") - ignore_list = CONFIG.get("ignore_list", []) - check_task_available_func = partial(need_to_generate, ignore_list=ignore_list) + check_task_available_func = partial(need_to_generate, ignore_list=setting.project.ignore_list) task_manager = self.meta_info.get_topology( check_task_available_func ) # 将按照此顺序生成文档 @@ -147,7 +120,7 @@ def first_generate(self): self.generate_doc_for_a_single_item, ), ) - for process_id in range(CONFIG["max_thread_count"]) + for process_id in range(setting.project.max_thread_count) ] for thread in threads: thread.start() @@ -159,9 +132,7 @@ def first_generate(self): ) self.meta_info.in_generation_process = False self.meta_info.checkpoint( - target_dir_path=os.path.join( - CONFIG["repo_path"], CONFIG["project_hierarchy"] - ) + target_dir_path=self.absolute_project_hierarchy_path ) logger.info( f"成功生成了 {before_task_len - len(task_manager.task_dict)} 个文档" @@ -176,8 +147,8 @@ def markdown_refresh(self): """将目前最新的document信息写入到一个markdown格式的文件夹里(不管markdown内容是不是变化了)""" with self.runner_lock: # 首先删除doc下所有内容,然后再重新写入 - markdown_folder = os.path.join(CONFIG["repo_path"],CONFIG["Markdown_Docs_folder"]) - if os.path.exists(markdown_folder): + markdown_folder = setting.project.target_repo / setting.project.markdown_docs_name + if markdown_folder.exists(): shutil.rmtree(markdown_folder) os.mkdir(markdown_folder) @@ -199,7 +170,6 @@ def recursive_check( continue rel_file_path = file_item.get_full_name() - # file_handler = FileHandler(CONFIG['repo_path'], rel_file_path) def to_markdown(item: DocItem, now_level: int) -> str: markdown_content = "" markdown_content += ( @@ -224,19 +194,19 @@ def to_markdown(item: DocItem, now_level: int) -> str: assert markdown != None, f"markdown内容为空,文件路径为{rel_file_path}" # 写入markdown内容到.md文件 file_path = os.path.join( - CONFIG["Markdown_Docs_folder"], + setting.project.markdown_docs_name, file_item.get_file_name().replace(".py", ".md"), ) if file_path.startswith("/"): # 移除开头的 '/' file_path = file_path[1:] - abs_file_path = os.path.join(CONFIG["repo_path"], file_path) + abs_file_path = setting.project.target_repo / file_path os.makedirs(os.path.dirname(abs_file_path), exist_ok=True) with open(abs_file_path, "w", encoding="utf-8") as file: file.write(markdown) logger.info( - f"markdown document has been refreshed at {CONFIG['Markdown_Docs_folder']}" + f"markdown document has been refreshed at {setting.project.markdown_docs_name}" ) def git_commit(self, commit_message): @@ -262,9 +232,7 @@ def run(self): # 根据document version自动检测是否仍在最初生成的process里(是否为第一次生成) self.first_generate() # 如果是第一次做文档生成任务,就通过first_generate生成所有文档 self.meta_info.checkpoint( - target_dir_path=os.path.join( - CONFIG["repo_path"], CONFIG["project_hierarchy"] - ), + target_dir_path=self.absolute_project_hierarchy_path, flash_reference_relation=True, ) # 这一步将生成后的meta信息(包含引用关系)写入到.project_doc_record文件夹中 return @@ -291,8 +259,7 @@ def run(self): self.meta_info.in_generation_process = True # 将in_generation_process设置为True,表示检测到变更后正在生成文档的过程中 # 处理任务队列 - ignore_list = CONFIG.get("ignore_list", []) - check_task_available_func = partial(need_to_generate, ignore_list=ignore_list) + check_task_available_func = partial(need_to_generate, ignore_list=setting.project.ignore_list) task_manager = self.meta_info.get_task_manager(self.meta_info.target_repo_hierarchical_tree,task_available_func=check_task_available_func) @@ -308,7 +275,7 @@ def run(self): target=worker, args=(task_manager, process_id, self.generate_doc_for_a_single_item), ) - for process_id in range(CONFIG["max_thread_count"]) + for process_id in range(setting.project.max_thread_count) ] for thread in threads: thread.start() @@ -319,9 +286,7 @@ def run(self): self.meta_info.document_version = self.change_detector.repo.head.commit.hexsha self.meta_info.checkpoint( - target_dir_path=os.path.join( - CONFIG["repo_path"], CONFIG["project_hierarchy"] - ), + target_dir_path=self.absolute_project_hierarchy_path, flash_reference_relation=True, ) logger.info(f"Doc has been forwarded to the latest version") @@ -383,7 +348,7 @@ def add_new_item(self, file_handler, json_data): file_handler.write_file( os.path.join( self.project_manager.repo_path, - CONFIG["Markdown_Docs_folder"], + setting.project.markdown_docs_name, file_handler.file_path.replace(".py", ".md"), ), markdown, @@ -441,7 +406,7 @@ def process_file_changes(self, repo_path, file_path, is_new_file): # 将markdown内容写入.md文件 file_handler.write_file( os.path.join( - CONFIG["Markdown_Docs_folder"], + setting.project.markdown_docs_name, file_handler.file_path.replace(".py", ".md"), ), markdown, diff --git a/repo_agent/settings.py b/repo_agent/settings.py index d23684c..23a6b04 100644 --- a/repo_agent/settings.py +++ b/repo_agent/settings.py @@ -1,19 +1,29 @@ -from pydantic_settings import BaseSettings +from enum import StrEnum + +from iso639 import Language, LanguageNotFoundError from pydantic import ( - PositiveFloat, + DirectoryPath, + Field, HttpUrl, + PositiveFloat, PositiveInt, - DirectoryPath, SecretStr, - field_validator, field_serializer, - Field, + field_validator, ) +from pydantic_settings import BaseSettings + from repo_agent.config_manager import read_config, write_config -from iso639 import Language, LanguageNotFoundError -from repo_agent.log import LogLevel, set_logger_level_from_config +class LogLevel(StrEnum): + DEBUG = 'DEBUG' + INFO = 'INFO' + WARNING = 'WARNING' + ERROR = 'ERROR' + CRITICAL = 'CRITICAL' + + class ProjectSettings(BaseSettings): target_repo: DirectoryPath = "" # type: ignore hierarchy_name: str = ".project_doc_record" @@ -69,7 +79,6 @@ class Setting(BaseSettings): _config_data = read_config() setting = Setting.model_validate(_config_data) -set_logger_level_from_config(log_level=setting.project.log_level) if _config_data == {}: write_config(setting.model_dump()) diff --git a/repo_agent/utils/meta_info_utils.py b/repo_agent/utils/meta_info_utils.py index 2d3cd52..9d36e95 100644 --- a/repo_agent/utils/meta_info_utils.py +++ b/repo_agent/utils/meta_info_utils.py @@ -1,11 +1,11 @@ +import itertools import os + import git from colorama import Fore, Style -import itertools -from typing import List -from repo_agent.config import CONFIG from repo_agent.log import logger +from repo_agent.settings import setting latest_verison_substring = "_latest_version.py" @@ -19,7 +19,7 @@ def make_fake_files(): """ delete_fake_files() - repo = git.Repo(CONFIG["repo_path"]) + repo = git.Repo(setting.project.target_repo) unstaged_changes = repo.index.diff(None) #在git status里,但是有修改没提交 untracked_files = repo.untracked_files #在文件系统里,但没在git里的文件 @@ -44,15 +44,15 @@ def make_fake_files(): if now_file_path.endswith(".py"): raw_file_content = diff_file.a_blob.data_stream.read().decode("utf-8") latest_file_path = now_file_path[:-3] + latest_verison_substring - if os.path.exists(os.path.join(CONFIG["repo_path"],now_file_path)): - os.rename(os.path.join(CONFIG["repo_path"],now_file_path), os.path.join(CONFIG["repo_path"], latest_file_path)) + if os.path.exists(os.path.join(setting.project.target_repo,now_file_path)): + os.rename(os.path.join(setting.project.target_repo,now_file_path), os.path.join(setting.project.target_repo, latest_file_path)) print(f"{Fore.LIGHTMAGENTA_EX}[Save Latest Version of Code]: {Style.RESET_ALL}{now_file_path} -> {latest_file_path}") else: print(f"{Fore.LIGHTMAGENTA_EX}[Create Temp-File for Deleted(But not Staged) Files]: {Style.RESET_ALL}{now_file_path} -> {latest_file_path}") - with open(os.path.join(CONFIG["repo_path"],latest_file_path), "w") as writer: + with open(os.path.join(setting.project.target_repo,latest_file_path), "w") as writer: pass - with open(os.path.join(CONFIG["repo_path"],now_file_path), "w") as writer: + with open(os.path.join(setting.project.target_repo,now_file_path), "w") as writer: writer.write(raw_file_content) file_path_reflections[now_file_path] = latest_file_path #real指向fake return file_path_reflections, jump_files @@ -72,10 +72,10 @@ def gci(filepath): origin_name = fi_d.replace(latest_verison_substring, ".py") os.remove(origin_name) if os.path.getsize(fi_d) == 0: - print(f"{Fore.LIGHTRED_EX}[Deleting Temp File]: {Style.RESET_ALL}{fi_d[len(CONFIG['repo_path']):]}, {origin_name[len(CONFIG['repo_path']):]}") + print(f"{Fore.LIGHTRED_EX}[Deleting Temp File]: {Style.RESET_ALL}{fi_d[len(setting.project.target_repo):]}, {origin_name[len(setting.project.target_repo):]}") os.remove(fi_d) else: - print(f"{Fore.LIGHTRED_EX}[Recovering Latest Version]: {Style.RESET_ALL}{origin_name[len(CONFIG['repo_path']):]} <- {fi_d[len(CONFIG['repo_path']):]}") + print(f"{Fore.LIGHTRED_EX}[Recovering Latest Version]: {Style.RESET_ALL}{origin_name[len(setting.project.target_repo):]} <- {fi_d[len(setting.project.target_repo):]}") os.rename(fi_d, origin_name) - gci(CONFIG["repo_path"]) \ No newline at end of file + gci(setting.project.target_repo) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index f58858c..cb04185 100644 --- a/requirements.txt +++ b/requirements.txt @@ -103,9 +103,9 @@ bcrypt==4.1.2 \ --hash=sha256:eb3bd3321517916696233b5e0c67fd7d6281f0ef48e66812db35fc963a422a1c \ --hash=sha256:f70d9c61f9c4ca7d57f3bfe88a5ccf62546ffbadf3681bb1e268d9d2e41c91a7 \ --hash=sha256:fbe188b878313d01b7718390f31528be4010fed1faa798c5a1d0469c9c48c369 -build==1.0.3 \ - --hash=sha256:538aab1b64f9828977f84bc63ae570b060a8ed1be419e7870b8b4fc5e6ea553b \ - --hash=sha256:589bf99a67df7c9cf07ec0ac0e5e2ea5d4b37ac63301c4986d1acb126aa83f8f +build==1.1.1 \ + --hash=sha256:8ed0851ee76e6e38adce47e4bee3b51c771d86c64cf578d0c2245567ee200e73 \ + --hash=sha256:8eea65bb45b1aac2e734ba2cc8dad3a6d97d97901a395bd0ed3e7b46953d2a31 cachetools==5.3.3 \ --hash=sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945 \ --hash=sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105 @@ -434,9 +434,9 @@ httptools==0.6.1 \ httpx==0.27.0 \ --hash=sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5 \ --hash=sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5 -huggingface-hub==0.21.2 \ - --hash=sha256:16955c2b60bcff32a0778f84b9e9ae8f61d7f003da6aa1fbb7bc897a0c37b28c \ - --hash=sha256:839f2fc69fc51797b76dcffa7edbf7fb1150176f74cb1dc2d87ca00e5e0b5611 +huggingface-hub==0.21.3 \ + --hash=sha256:26a15b604e4fc7bad37c467b76456543ec849386cbca9cd7e1e135f53e500423 \ + --hash=sha256:b183144336fdf2810a8c109822e0bb6ef1fd61c65da6fb60e8c3f658b7144016 humanfriendly==10.0 \ --hash=sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477 \ --hash=sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc @@ -576,9 +576,9 @@ markupsafe==2.1.5 \ --hash=sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b \ --hash=sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5 \ --hash=sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd -marshmallow==3.21.0 \ - --hash=sha256:20f53be28c6e374a711a16165fb22a8dc6003e3f7cda1285e3ca777b9193885b \ - --hash=sha256:e7997f83571c7fd476042c2c188e4ee8a78900ca5e74bd9c8097afa56624e9bd +marshmallow==3.21.1 \ + --hash=sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3 \ + --hash=sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633 matplotlib==3.8.3 \ --hash=sha256:04b36ad07eac9740fc76c2aa16edf94e50b297d6eb4c081e3add863de4bb19a7 \ --hash=sha256:09074f8057917d17ab52c242fdf4916f30e99959c1908958b1fc6032e2d0f6d4 \ @@ -916,9 +916,9 @@ pillow==10.2.0 \ pluggy==1.4.0 \ --hash=sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981 \ --hash=sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be -posthog==3.4.2 \ - --hash=sha256:c7e79b2e585d16e93749874bcbcdad78d857037398ce0d8d6c474a04d0bd3bbe \ - --hash=sha256:f0eafa663fbc4a942b49b6168a62a890635407044bbc7593051dcb9cc1208873 +posthog==3.5.0 \ + --hash=sha256:3c672be7ba6f95d555ea207d4486c171d06657eb34b3ce25eb043bfe7b6b5b76 \ + --hash=sha256:8f7e3b2c6e8714d0c0c542a2109b83a7549f63b7113a133ab2763a89245ef2ef prettytable==3.10.0 \ --hash=sha256:6536efaf0757fdaa7d22e78b3aac3b69ea1b7200538c2c6995d649365bddab92 \ --hash=sha256:9665594d137fb08a1117518c25551e0ede1687197cf353a4fdc78d27e1073568 @@ -1014,6 +1014,9 @@ pydantic-core==2.16.3 \ --hash=sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120 \ --hash=sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f \ --hash=sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a +pydantic-settings==2.2.1 \ + --hash=sha256:00b9f6a5e95553590434c0fa01ead0b216c3e10bc54ae02e37f359948643c5ed \ + --hash=sha256:0235391d26db4d2190cb9b31051c4b46882d28a51533f97440867f012d4da091 pydub==0.25.1 \ --hash=sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6 \ --hash=sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f @@ -1037,12 +1040,15 @@ pytest==7.4.4 \ pytest-mock==3.12.0 \ --hash=sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f \ --hash=sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9 -python-dateutil==2.8.2 \ - --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ - --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 +python-dateutil==2.9.0.post0 \ + --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ + --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 python-dotenv==1.0.1 \ --hash=sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca \ --hash=sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a +python-iso639==2024.2.7 \ + --hash=sha256:7b149623ff74230f4ee3061fb01d18e57a8d07c5fee2aa72907f39b7f6d16cbc \ + --hash=sha256:c323233348c34d57c601e3e6d824088e492896bcb97a61a87f7d93401a305377 python-multipart==0.0.9 \ --hash=sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026 \ --hash=sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215 @@ -1131,9 +1137,9 @@ requests==2.31.0 \ requests-oauthlib==1.3.1 \ --hash=sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5 \ --hash=sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a -rich==13.7.0 \ - --hash=sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa \ - --hash=sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235 +rich==13.7.1 \ + --hash=sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222 \ + --hash=sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432 rpds-py==0.18.0 \ --hash=sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f \ --hash=sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c \ @@ -1211,24 +1217,24 @@ rpds-py==0.18.0 \ rsa==4.9 \ --hash=sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7 \ --hash=sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21 -ruff==0.2.2 \ - --hash=sha256:0a9efb032855ffb3c21f6405751d5e147b0c6b631e3ca3f6b20f917572b97eb6 \ - --hash=sha256:0c126da55c38dd917621552ab430213bdb3273bb10ddb67bc4b761989210eb6e \ - --hash=sha256:1695700d1e25a99d28f7a1636d85bafcc5030bba9d0578c0781ba1790dbcf51c \ - --hash=sha256:1ec49be4fe6ddac0503833f3ed8930528e26d1e60ad35c2446da372d16651ce9 \ - --hash=sha256:3b65494f7e4bed2e74110dac1f0d17dc8e1f42faaa784e7c58a98e335ec83d7e \ - --hash=sha256:5e1439c8f407e4f356470e54cdecdca1bd5439a0673792dbe34a2b0a551a2fe3 \ - --hash=sha256:5e22676a5b875bd72acd3d11d5fa9075d3a5f53b877fe7b4793e4673499318ba \ - --hash=sha256:6a61ea0ff048e06de273b2e45bd72629f470f5da8f71daf09fe481278b175001 \ - --hash=sha256:940de32dc8853eba0f67f7198b3e79bc6ba95c2edbfdfac2144c8235114d6726 \ - --hash=sha256:b0c232af3d0bd8f521806223723456ffebf8e323bd1e4e82b0befb20ba18388e \ - --hash=sha256:c9d15fc41e6054bfc7200478720570078f0b41c9ae4f010bcc16bd6f4d1aacdd \ - --hash=sha256:cc9a91ae137d687f43a44c900e5d95e9617cb37d4c989e462980ba27039d239d \ - --hash=sha256:d450b7fbff85913f866a5384d8912710936e2b96da74541c82c1b458472ddb39 \ - --hash=sha256:d920499b576f6c68295bc04e7b17b6544d9d05f196bb3aac4358792ef6f34325 \ - --hash=sha256:e62ed7f36b3068a30ba39193a14274cd706bc486fad521276458022f7bccb31d \ - --hash=sha256:ecd46e3106850a5c26aee114e562c329f9a1fbe9e4821b008c4404f64ff9ce73 \ - --hash=sha256:f63d96494eeec2fc70d909393bcd76c69f35334cdbd9e20d089fb3f0640216ca +ruff==0.3.0 \ + --hash=sha256:0886184ba2618d815067cf43e005388967b67ab9c80df52b32ec1152ab49f53a \ + --hash=sha256:128265876c1d703e5f5e5a4543bd8be47c73a9ba223fd3989d4aa87dd06f312f \ + --hash=sha256:19eacceb4c9406f6c41af806418a26fdb23120dfe53583df76d1401c92b7c14b \ + --hash=sha256:23dbb808e2f1d68eeadd5f655485e235c102ac6f12ad31505804edced2a5ae77 \ + --hash=sha256:2f7dbba46e2827dfcb0f0cc55fba8e96ba7c8700e0a866eb8cef7d1d66c25dcb \ + --hash=sha256:3ef655c51f41d5fa879f98e40c90072b567c666a7114fa2d9fe004dffba00932 \ + --hash=sha256:5da894a29ec018a8293d3d17c797e73b374773943e8369cfc50495573d396933 \ + --hash=sha256:755c22536d7f1889be25f2baf6fedd019d0c51d079e8417d4441159f3bcd30c2 \ + --hash=sha256:7deb528029bacf845bdbb3dbb2927d8ef9b4356a5e731b10eef171e3f0a85944 \ + --hash=sha256:9343690f95710f8cf251bee1013bf43030072b9f8d012fbed6ad702ef70d360a \ + --hash=sha256:a1f3ed501a42f60f4dedb7805fa8d4534e78b4e196f536bac926f805f0743d49 \ + --hash=sha256:b08b356d06a792e49a12074b62222f9d4ea2a11dca9da9f68163b28c71bf1dd4 \ + --hash=sha256:cc30a9053ff2f1ffb505a585797c23434d5f6c838bacfe206c0e6cf38c921a1e \ + --hash=sha256:d0d3d7ef3d4f06433d592e5f7d813314a34601e6c5be8481cccb7fa760aa243e \ + --hash=sha256:dd73fe7f4c28d317855da6a7bc4aa29a1500320818dd8f27df95f70a01b8171f \ + --hash=sha256:e1e0d4381ca88fb2b73ea0766008e703f33f460295de658f5467f6f229658c19 \ + --hash=sha256:e3a4a6d46aef0a84b74fcd201a4401ea9a6cd85614f6a9435f2d33dd8cefbf83 semantic-version==2.10.0 \ --hash=sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c \ --hash=sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177 @@ -1247,33 +1253,33 @@ smmap==5.0.1 \ sniffio==1.3.1 \ --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc -sqlalchemy==2.0.27 \ - --hash=sha256:03f448ffb731b48323bda68bcc93152f751436ad6037f18a42b7e16af9e91c07 \ - --hash=sha256:0de1263aac858f288a80b2071990f02082c51d88335a1db0d589237a3435fe71 \ - --hash=sha256:0fb3bffc0ced37e5aa4ac2416f56d6d858f46d4da70c09bb731a246e70bff4d5 \ - --hash=sha256:1306102f6d9e625cebaca3d4c9c8f10588735ef877f0360b5cdb4fdfd3fd7131 \ - --hash=sha256:15e19a84b84528f52a68143439d0c7a3a69befcd4f50b8ef9b7b69d2628ae7c4 \ - --hash=sha256:1ab4e0448018d01b142c916cc7119ca573803a4745cfe341b8f95657812700ac \ - --hash=sha256:4535c49d961fe9a77392e3a630a626af5baa967172d42732b7a43496c8b28876 \ - --hash=sha256:5b78aa9f4f68212248aaf8943d84c0ff0f74efc65a661c2fc68b82d498311fd5 \ - --hash=sha256:5cd20f58c29bbf2680039ff9f569fa6d21453fbd2fa84dbdb4092f006424c2e6 \ - --hash=sha256:680b9a36029b30cf063698755d277885d4a0eab70a2c7c6e71aab601323cba45 \ - --hash=sha256:6c5bad7c60a392850d2f0fee8f355953abaec878c483dd7c3836e0089f046bf6 \ - --hash=sha256:6c7a596d0be71b7baa037f4ac10d5e057d276f65a9a611c46970f012752ebf2d \ - --hash=sha256:7f470327d06400a0aa7926b375b8e8c3c31d335e0884f509fe272b3c700a7254 \ - --hash=sha256:86a6ed69a71fe6b88bf9331594fa390a2adda4a49b5c06f98e47bf0d392534f8 \ - --hash=sha256:8dfc936870507da96aebb43e664ae3a71a7b96278382bcfe84d277b88e379b18 \ - --hash=sha256:954d9735ee9c3fa74874c830d089a815b7b48df6f6b6e357a74130e478dbd951 \ - --hash=sha256:a3012ab65ea42de1be81fff5fb28d6db893ef978950afc8130ba707179b4284a \ - --hash=sha256:c4fbe6a766301f2e8a4519f4500fe74ef0a8509a59e07a4085458f26228cd7cc \ - --hash=sha256:ce850db091bf7d2a1f2fdb615220b968aeff3849007b1204bf6e3e50a57b3d32 \ - --hash=sha256:d04e579e911562f1055d26dab1868d3e0bb905db3bccf664ee8ad109f035618a \ - --hash=sha256:d177b7e82f6dd5e1aebd24d9c3297c70ce09cd1d5d37b43e53f39514379c029c \ - --hash=sha256:d997c5938a08b5e172c30583ba6b8aad657ed9901fc24caf3a7152eeccb2f1b4 \ - --hash=sha256:dbcd77c4d94b23e0753c5ed8deba8c69f331d4fd83f68bfc9db58bc8983f49cd \ - --hash=sha256:eb15ef40b833f5b2f19eeae65d65e191f039e71790dd565c2af2a3783f72262f \ - --hash=sha256:f9374e270e2553653d710ece397df67db9d19c60d2647bcd35bfc616f1622dcd \ - --hash=sha256:fa67d821c1fd268a5a87922ef4940442513b4e6c377553506b9db3b83beebbd8 +sqlalchemy==2.0.28 \ + --hash=sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2 \ + --hash=sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa \ + --hash=sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462 \ + --hash=sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d \ + --hash=sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc \ + --hash=sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da \ + --hash=sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2 \ + --hash=sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368 \ + --hash=sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f \ + --hash=sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986 \ + --hash=sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5 \ + --hash=sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc \ + --hash=sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5 \ + --hash=sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c \ + --hash=sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6 \ + --hash=sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9 \ + --hash=sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385 \ + --hash=sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c \ + --hash=sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67 \ + --hash=sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02 \ + --hash=sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a \ + --hash=sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097 \ + --hash=sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6 \ + --hash=sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252 \ + --hash=sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9 \ + --hash=sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71 starlette==0.36.3 \ --hash=sha256:13d429aa93a61dc40bf503e8c801db1f1bca3dc706b10ef2434a36123568f044 \ --hash=sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080 @@ -1381,9 +1387,12 @@ tokenizers==0.15.2 \ --hash=sha256:f33dfbdec3784093a9aebb3680d1f91336c56d86cc70ddf88708251da1fe9064 \ --hash=sha256:f86593c18d2e6248e72fb91c77d413a815153b8ea4e31f7cd443bdf28e467670 \ --hash=sha256:fb16ba563d59003028b678d2361a27f7e4ae0ab29c7a80690efa20d829c81fdb -tomli==2.0.1; python_version < "3.11" \ +tomli==2.0.1 \ --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f +tomli-w==1.0.0 \ + --hash=sha256:9f2a07e8be30a0729e533ec968016807069991ae2fd921a78d42f429ae5f4463 \ + --hash=sha256:f463434305e0336248cac9c2dc8076b707d8a12d019dd349f5c1e382dd1ae1b9 tomlkit==0.12.0 \ --hash=sha256:01f0477981119c7d8ee0f67ebe0297a7c95b14cf9f4b102b45486deb77018716 \ --hash=sha256:926f1f37a1587c7a4f6c7484dae538f1345d96d793d9adab5d3675957b1d0766 From 7ffcc1b186cde1665bbbfabd98f2039cb748d3b4 Mon Sep 17 00:00:00 2001 From: Umpire2018 <138990495+Umpire2018@users.noreply.github.com> Date: Wed, 6 Mar 2024 08:04:29 +0000 Subject: [PATCH 07/11] docs: update `README.md` and `README_CN.md` - Add `chat-with-repo` to cli --- README.md | 200 ++++++++++------------ README_CN.md | 233 ++++++++++++++------------ repo_agent/chat_with_repo/__init__.py | 3 + repo_agent/chat_with_repo/main.py | 12 +- repo_agent/main.py | 9 +- repo_agent/settings.py | 29 ++-- 6 files changed, 248 insertions(+), 238 deletions(-) diff --git a/README.md b/README.md index 425307f..72e8190 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,33 @@ -[中文](README_CN.md) | [Background](#-background) | [Features](#-features) | [Getting Started](#-getting-started) | [Future Work](#-future-work) | [Supported Language](#-supported-language) | [Citation](#-citation) - -# 🤗 Introduction - -RepoAgent is an Open-Source project driven by Large Language Models(LLMs) that aims to provide an intelligent way to document projects. -It is designed to be a handy tool for developers who need to organize their code and cooperate with teammates. - -**Paper:** http://arxiv.org/abs/2402.16667 - -![RepoAgent](assets/images/RepoAgent.png) +
+
+
+ English readme + • + 简体中文 readme +
## 👾 Background + In the realm of computer programming, the significance of comprehensive project documentation, including detailed explanations for each Python file, cannot be overstated. Such documentation serves as the cornerstone for understanding, maintaining, and enhancing the codebase. It provides essential context and rationale for the code, making it easier for current and future developers to comprehend the purpose, functionality, and structure of the software. It not only facilitates current and future developers in grasping the project's purpose and structure but also ensures that the project remains accessible and modifiable over time, significantly easing the learning curve for new team members. Traditionally, creating and maintaining software documentation demanded significant human effort and expertise, a challenge for small teams without dedicated personnel. The introduction of Large Language Models (LLMs) like GPT has transformed this, enabling AI to handle much of the documentation process. This shift allows human developers to focus on verification and fine-tuning, greatly reducing the manual burden of documentation. @@ -31,13 +49,13 @@ Traditionally, creating and maintaining software documentation demanded signific ### Installation Method - +``` #### Development Setup Using PDM @@ -56,7 +74,7 @@ If you're looking to contribute or set up a development environment: ```bash git clone https://github.com/LOGIC-10/RepoAgent.git cd RepoAgent -``` + ``` - **Setup with PDM** @@ -76,75 +94,81 @@ If you're looking to contribute or set up a development environment: ### Configuring RepoAgent -First, configure the OpenAI API parameters in the config.yml file. -For details on obtaining these, please refer to [OpenAI API](https://beta.openai.com/docs/developer-quickstart/your-api-keys). - -In the `config.yml` file, configure other parameters like OpenAI API, the destination repository path, document language, and so on: - -```yaml -api_keys: - gpt-3.5-turbo-16k: - - api_key: sk-XXXX - base_url: https://example.com/v1/ - api_type: azure - api_version: XXX - engine: GPT-35-Turbo-16k - # you can use any kwargs supported by openai.ChatCompletion here - - api_key: sk-xxxxx - organization: org-xxxxxx - model: gpt-3.5-turbo-16k - ... - -default_completion_kwargs: - model: gpt-4-1106 - temperature: 0.2 - request_timeout: 60 - -repo_path: /path/to/your/repo -project_hierarchy: .project_hierarchy # This is a folder, where we store the project hierarchy and metainfo. This can be shared with your team members. -Markdown_Docs_folder: Markdown_Docs # The folder in the root directory of your target repository to store the documentation. -ignore_list: ["ignore_file1.py", "ignore_file2.py", "ignore_directory"] # Ignore some py files or folders that you don't want to generate documentation for by giving relative paths in ignore_list. - -language: en # Two-letter language codes (ISO 639-1 codes), e.g. `language: en` for English. Refer to Supported Language for more languages. -max_thread_count: 10 # We support multiprocessing to speedup the process -max_document_tokens: 1024 # the maximum number of tokens in a document generated -log_level: info +You can use RepoAgent with OpenAI API for the moment. + +```sh +export OPENAI_API_KEY=YOUR_API_KEY # on Linux/Mac +set OPENAI_API_KEY=YOUR_API_KEY # on Windows +$Env:OPENAI_API_KEY = "YOUR_API_KEY" # on Windows (PowerShell) +``` + +Use `repoagent configure` if you need to running parameters. + +```sh +Enter the path to target repository: +Enter the project hierarchy file name [.project_doc_record]: +Enter the Markdown documents folder name [markdown_docs]: +Enter files or directories to ignore, separated by commas []: +Enter the language (ISO 639 code or language name, e.g., 'en', 'eng', 'English') [Chinese]: +Enter the maximum number of threads [4]: +Enter the maximum number of document tokens [1024]: +Enter the log level (DEBUG, INFO, WARNING, ERROR, CRITICAL) [INFO]: +Enter the model [gpt-3.5-turbo]: +Enter the temperature [0.2]: +Enter the request timeout (seconds) [60.0]: +Enter the base URL [https://api.openai.com/v1]: ``` -### Run RepoAgent +## Run RepoAgent Enter the root directory of RepoAgent and try the following command in the terminal: ```sh -python -m repo_agent #this command will generate doc, or update docs(pre-commit-hook will automatically call this) +repo_agent run #this command will generate doc, or update docs(pre-commit-hook will automatically call this) +``` + +The run command supports the following optional flags (if set, will override config defaults): + +- `-m`, `--model` TEXT: Specifies the model to use for completion. Default: `gpt-3.5-turbo` +- `-t`, `--temperature` FLOAT: Sets the generation temperature for the model. Lower values make the model more deterministic. Default: `0.2` +- `-r`, `--request-timeout` INTEGER: Defines the timeout in seconds for the API request. Default: `60` +- `-b`, `--base-url` TEXT: The base URL for the API calls. Default: `https://api.openai.com/v1` +- `-tp`, `--target-repo-path` PATH: The file system path to the target repository. Used as the root for documentation generation. Default: `/home/test/arno` +- `-hp`, `--hierarchy-path` TEXT: The name or path for the project hierarchy file, used to organize documentation structure. Default: `.project_doc_record` +- `-mdp`, `--markdown-docs-path` TEXT: The folder path where Markdown documentation will be stored or generated. Default: `markdown_docs` +- `-i`, `--ignore-list` TEXT: A list of files or directories to ignore during documentation generation, separated by commas. +- `-l`, `--language` TEXT: The ISO 639 code or language name for the documentation. Default: `Chinese` +- `-ll`, `--log-level` [DEBUG|INFO|WARNING|ERROR|CRITICAL]: Sets the logging level for the application. Default: `INFO` + + +You can also try the following feature -# you can also try the follow feature -python -m repo_agent clean #this command will remove repoagent-related cache -python -m repo_agent print #this command will print how repo-agent parse the target repo -python -m repo_agent diff #this command will check what docs will be updated/generated based on current code change +```sh +repo_agent clean # Remove repoagent-related cache +repo_agent print-hierarchy # Print how repo-agent parse the target repo +repo_agent diff # Check what docs will be updated/generated based on current code change ``` If it's your first time generating documentation for the target repository, RepoAgent will automatically create a JSON file maintaining the global structure information and a folder named Markdown_Docs in the root directory of the target repository for storing documents. -The paths of the global structure information json file and the documentation folder can be configured in `config.yml`. - -Once you have initially generated the global documentation for the target repository, or if the project you cloned already contains global documentation information, you can then seamlessly and automatically maintain internal project documentation with your team by configuring the **pre-commit hook** in the target repository! +Once you have initially generated the global documentation for the target repository, or if the project you cloned already contains global documentation information, you can then seamlessly and automatically maintain internal project documentation with your team by configuring the **pre-commit hook** in the target repository! -### Configuring the Target Repository +### Use `pre-commit` RepoAgent currently supports generating documentation for projects, which requires some configuration in the target repository. First, ensure that the target repository is a git repository and has been initialized. -``` + +```sh git init ``` Install pre-commit in the target repository to detect changes in the git repository. -``` +```sh pip install pre-commit ``` Create a file named `.pre-commit-config.yaml` in the root directory of the target repository. An example is as follows: -``` +```yml repos: - repo: local hooks: @@ -155,11 +179,14 @@ repos: # You can specify the file types that trigger the hook, but currently only python is supported. types: [python] ``` + For specific configuration methods of hooks, please refer to [pre-commit](https://pre-commit.com/#plugins). After configuring the yaml file, execute the following command to install the hook. -``` + +```sh pre-commit install ``` + In this way, each git commit will trigger the RepoAgent's hook, automatically detecting changes in the target repository and generating corresponding documents. Next, you can make some modifications to the target repository, such as adding a new file to the target repository, or modifying an existing file. You just need to follow the normal git workflow: git add, git commit -m "your commit message", git push @@ -177,25 +204,21 @@ We utilized the default model **gpt-3.5-turbo** to generate documentation for th **In the end, you can flexibly adjust the output format, template, and other aspects of the document by customizing the prompt. We are excited about your exploration of a more scientific approach to Automated Technical Writing and your contributions to the community.** ### Exploring chat with repo + We conceptualize **Chat With Repo** as a unified gateway for these downstream applications, acting as a connector that links RepoAgent to human users and other AI agents. Our future research will focus on adapting the interface to various downstream applications and customizing it to meet their unique characteristics and implementation requirements. Here we demonstrate a preliminary prototype of one of our downstream tasks: Automatic Q&A for Issues and Code Explanation. You can start the server by running the following code. -```bash -python -m repo_agent.chat_with_repo + +```sh +repoagent chat-with-repo ``` ## ✅ Future Work -- [x] Identification and maintenance of parent-child relationship hierarchy structure between objects -- [x] Implement Black commit -- [x] **Bi-direct reference** Construct Bi-directional reference topology -- [x] **chat with repo** Chat with the repository by giving code and document at the same time -- [x] Automatically generate better visualizations such as Gitbook +- [x] Support install and configure via `pip install repoagent` - [ ] Generate README.md automatically combining with the global documentation - [ ] **Multi-programming-language support** Support more programming languages like Java, C or C++, etc. - [ ] Local model support like Llama, chatGLM, Qwen, GLM4, etc. -- [ ] Support install and configure via `pip install repoagent` -- [X] Automatically generate Gitbook for better visualization effects ## 🥰 Featured Cases @@ -205,45 +228,6 @@ Here are featured cases that have adopted RepoAgent. - [ChatDev](https://github.com/OpenBMB/ChatDev): Collaborative AI agents for software development. - [XAgent](https://github.com/OpenBMB/XAgent): An Autonomous LLM Agent for Complex Task Solving. -## 🇺🇳 Supported Language - -Set the target language with the two-letter language codes (ISO 639-1 codes), Click on the 'Languages List' section below to expand the list of supported languages. - -
+
+
@@ -123,7 +123,7 @@ Enter the base URL [https://api.openai.com/v1]:
Enter the root directory of RepoAgent and try the following command in the terminal:
```sh
-repo_agent run #this command will generate doc, or update docs(pre-commit-hook will automatically call this)
+repoagent run #this command will generate doc, or update docs(pre-commit-hook will automatically call this)
```
The run command supports the following optional flags (if set, will override config defaults):
@@ -174,7 +174,7 @@ repos:
hooks:
- id: repo-agent
name: RepoAgent
- entry: python path/to/your/repo_agent/runner.py
+ entry: repoagent
language: system
# You can specify the file types that trigger the hook, but currently only python is supported.
types: [python]
diff --git a/README_CN.md b/README_CN.md
index e8b283a..e9dba11 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -1,4 +1,4 @@
-
@@ -21,7 +21,7 @@
- 英文说明 + English README • 简体中文说明
@@ -134,9 +134,9 @@ run 命令支持以下可选标志(如果设置,将覆盖配置默认值) 你也可以尝试以下功能 ```sh -python -m repo_agent clean # 此命令将删除与repoagent相关的缓存 -python -m repo_agent print # 此命令将打印repo-agent如何解析目标仓库 -python -m repo_agent diff # 此命令将检查基于当前代码更改将更新/生成哪些文档 +repoagent clean # 此命令将删除与repoagent相关的缓存 +repoagent print # 此命令将打印repo-agent如何解析目标仓库 +repoagent diff # 此命令将检查基于当前代码更改将更新/生成哪些文档 ``` 如果您是第一次对目标仓库生成文档,此时RepoAgent会自动生成一个维护全局结构信息的json文件,并在目标仓库根目录下创建一个名为Markdown_Docs的文件夹,用于存放文档。 @@ -163,7 +163,7 @@ repos: hooks: - id: repo-agent name: RepoAgent - entry: python path/to/your/repo_agent/runner.py + entry: repoagent language: system # 可以指定钩子触发的文件类型,但是目前只支持python types: [python] diff --git a/pdm.lock b/pdm.lock index 478302b..e545bb7 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "test", "dev"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:2a8bb1010ba8d612216131d444ae4ea34316fb63094ad4e87b885936f014ea70" +content_hash = "sha256:42d6e2d804df1e83e58d4784fb71c6c613cb2679f5225bfb545063a045d14189" [[package]] name = "aiofiles" @@ -582,12 +582,12 @@ files = [ [[package]] name = "flatbuffers" -version = "23.5.26" +version = "24.3.7" summary = "The FlatBuffers serialization format for Python" groups = ["default"] files = [ - {file = "flatbuffers-23.5.26-py2.py3-none-any.whl", hash = "sha256:c0ff356da363087b915fde4b8b45bdda73432fc17cddb3c8157472eab1422ad1"}, - {file = "flatbuffers-23.5.26.tar.gz", hash = "sha256:9ea1144cac05ce5d86e2859f431c6cd5e66cd9c78c558317c7955fb8d4c78d89"}, + {file = "flatbuffers-24.3.7-py2.py3-none-any.whl", hash = "sha256:80c4f5dcad0ee76b7e349671a0d657f2fbba927a0244f88dd3f5ed6a3694e1fc"}, + {file = "flatbuffers-24.3.7.tar.gz", hash = "sha256:0895c22b9a6019ff2f4de2e5e2f7cd15914043e6e7033a94c0c6369422690f22"}, ] [[package]] @@ -752,7 +752,7 @@ files = [ [[package]] name = "gradio" -version = "4.19.2" +version = "4.20.1" requires_python = ">=3.8" summary = "Python library for easily interacting with trained machine learning models" groups = ["default"] @@ -761,7 +761,7 @@ dependencies = [ "altair<6.0,>=4.2.0", "fastapi", "ffmpy", - "gradio-client==0.10.1", + "gradio-client==0.11.0", "httpx>=0.24.1", "huggingface-hub>=0.19.3", "importlib-resources<7.0,>=1.3", @@ -785,13 +785,13 @@ dependencies = [ "uvicorn>=0.14.0", ] files = [ - {file = "gradio-4.19.2-py3-none-any.whl", hash = "sha256:acab4a35f556dbc3ae637469312738d154bcb73f0b8d5f4f65e4d067ecb1e0b1"}, - {file = "gradio-4.19.2.tar.gz", hash = "sha256:6fe5815bb4dfaeed1fc74223bffd91da70a1b463158af8c5e03d01bb09068a1d"}, + {file = "gradio-4.20.1-py3-none-any.whl", hash = "sha256:01815047593f8d653d609ab2d9b89a7d435fd20438602de19278f8cd6acd1fb9"}, + {file = "gradio-4.20.1.tar.gz", hash = "sha256:9efb88a4e1626f4f452467e45f44c37dbd8b57f7839f71326ec169e40de5cdab"}, ] [[package]] name = "gradio-client" -version = "0.10.1" +version = "0.11.0" requires_python = ">=3.8" summary = "Python library for easily interacting with trained machine learning models" groups = ["default"] @@ -804,8 +804,8 @@ dependencies = [ "websockets<12.0,>=10.0", ] files = [ - {file = "gradio_client-0.10.1-py3-none-any.whl", hash = "sha256:a0413fffdde3360e0f6aaec8b8c23d8a320049a571de2d111d85ebd295002165"}, - {file = "gradio_client-0.10.1.tar.gz", hash = "sha256:879eb56fae5d6b1603bb9375b88d1de0d034f3dac4b3afc8dbc66f36f6e54d5d"}, + {file = "gradio_client-0.11.0-py3-none-any.whl", hash = "sha256:638a43ccc73937790a3c68adb732da03ee7077732a0b6540a9196bacbb1f6539"}, + {file = "gradio_client-0.11.0.tar.gz", hash = "sha256:18ae178f83df4a1ff53247457f6ee13c4d0dfc3b7a8bff6d9da677600f31b04e"}, ] [[package]] @@ -959,7 +959,7 @@ files = [ [[package]] name = "huggingface-hub" -version = "0.21.3" +version = "0.21.4" requires_python = ">=3.8.0" summary = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" groups = ["default"] @@ -973,8 +973,8 @@ dependencies = [ "typing-extensions>=3.7.4.3", ] files = [ - {file = "huggingface_hub-0.21.3-py3-none-any.whl", hash = "sha256:b183144336fdf2810a8c109822e0bb6ef1fd61c65da6fb60e8c3f658b7144016"}, - {file = "huggingface_hub-0.21.3.tar.gz", hash = "sha256:26a15b604e4fc7bad37c467b76456543ec849386cbca9cd7e1e135f53e500423"}, + {file = "huggingface_hub-0.21.4-py3-none-any.whl", hash = "sha256:df37c2c37fc6c82163cdd8a67ede261687d80d1e262526d6c0ce73b6b3630a7b"}, + {file = "huggingface_hub-0.21.4.tar.gz", hash = "sha256:e1f4968c93726565a80edf6dc309763c7b546d0cfe79aa221206034d50155531"}, ] [[package]] @@ -1018,13 +1018,13 @@ files = [ [[package]] name = "importlib-resources" -version = "6.1.2" +version = "6.1.3" requires_python = ">=3.8" summary = "Read resources from Python packages" groups = ["default"] files = [ - {file = "importlib_resources-6.1.2-py3-none-any.whl", hash = "sha256:9a0a862501dc38b68adebc82970140c9e4209fc99601782925178f8386339938"}, - {file = "importlib_resources-6.1.2.tar.gz", hash = "sha256:308abf8474e2dba5f867d279237cd4076482c3de7104a40b41426370e891549b"}, + {file = "importlib_resources-6.1.3-py3-none-any.whl", hash = "sha256:4c0269e3580fe2634d364b39b38b961540a7738c02cb984e98add8b4221d793d"}, + {file = "importlib_resources-6.1.3.tar.gz", hash = "sha256:56fb4525197b78544a3354ea27793952ab93f935bb4bf746b846bb1015020f2b"}, ] [[package]] @@ -2214,13 +2214,13 @@ files = [ [[package]] name = "pyparsing" -version = "3.1.1" +version = "3.1.2" requires_python = ">=3.6.8" summary = "pyparsing module - Classes and methods to define and execute parsing grammars" groups = ["default"] files = [ - {file = "pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"}, - {file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"}, + {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, + {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, ] [[package]] @@ -2596,28 +2596,28 @@ files = [ [[package]] name = "ruff" -version = "0.3.0" +version = "0.3.1" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["default", "dev"] files = [ - {file = "ruff-0.3.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7deb528029bacf845bdbb3dbb2927d8ef9b4356a5e731b10eef171e3f0a85944"}, - {file = "ruff-0.3.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e1e0d4381ca88fb2b73ea0766008e703f33f460295de658f5467f6f229658c19"}, - {file = "ruff-0.3.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f7dbba46e2827dfcb0f0cc55fba8e96ba7c8700e0a866eb8cef7d1d66c25dcb"}, - {file = "ruff-0.3.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:23dbb808e2f1d68eeadd5f655485e235c102ac6f12ad31505804edced2a5ae77"}, - {file = "ruff-0.3.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ef655c51f41d5fa879f98e40c90072b567c666a7114fa2d9fe004dffba00932"}, - {file = "ruff-0.3.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d0d3d7ef3d4f06433d592e5f7d813314a34601e6c5be8481cccb7fa760aa243e"}, - {file = "ruff-0.3.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b08b356d06a792e49a12074b62222f9d4ea2a11dca9da9f68163b28c71bf1dd4"}, - {file = "ruff-0.3.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9343690f95710f8cf251bee1013bf43030072b9f8d012fbed6ad702ef70d360a"}, - {file = "ruff-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1f3ed501a42f60f4dedb7805fa8d4534e78b4e196f536bac926f805f0743d49"}, - {file = "ruff-0.3.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:cc30a9053ff2f1ffb505a585797c23434d5f6c838bacfe206c0e6cf38c921a1e"}, - {file = "ruff-0.3.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5da894a29ec018a8293d3d17c797e73b374773943e8369cfc50495573d396933"}, - {file = "ruff-0.3.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:755c22536d7f1889be25f2baf6fedd019d0c51d079e8417d4441159f3bcd30c2"}, - {file = "ruff-0.3.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:dd73fe7f4c28d317855da6a7bc4aa29a1500320818dd8f27df95f70a01b8171f"}, - {file = "ruff-0.3.0-py3-none-win32.whl", hash = "sha256:19eacceb4c9406f6c41af806418a26fdb23120dfe53583df76d1401c92b7c14b"}, - {file = "ruff-0.3.0-py3-none-win_amd64.whl", hash = "sha256:128265876c1d703e5f5e5a4543bd8be47c73a9ba223fd3989d4aa87dd06f312f"}, - {file = "ruff-0.3.0-py3-none-win_arm64.whl", hash = "sha256:e3a4a6d46aef0a84b74fcd201a4401ea9a6cd85614f6a9435f2d33dd8cefbf83"}, - {file = "ruff-0.3.0.tar.gz", hash = "sha256:0886184ba2618d815067cf43e005388967b67ab9c80df52b32ec1152ab49f53a"}, + {file = "ruff-0.3.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6b82e3937d0d76554cd5796bc3342a7d40de44494d29ff490022d7a52c501744"}, + {file = "ruff-0.3.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ae7954c8f692b70e6a206087ae3988acc9295d84c550f8d90b66c62424c16771"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b730f56ccf91225da0f06cfe421e83b8cc27b2a79393db9c3df02ed7e2bbc01"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c78bfa85637668f47bd82aa2ae17de2b34221ac23fea30926f6409f9e37fc927"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6abaad602d6e6daaec444cbf4d9364df0a783e49604c21499f75bb92237d4af"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5f0c21b6914c3c9a25a59497cbb1e5b6c2d8d9beecc9b8e03ee986e24eee072e"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:434c3fc72e6311c85cd143c4c448b0e60e025a9ac1781e63ba222579a8c29200"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78a7025e6312cbba496341da5062e7cdd47d95f45c1b903e635cdeb1ba5ec2b9"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52b02bb46f1a79b0c1fa93f6495bc7e77e4ef76e6c28995b4974a20ed09c0833"}, + {file = "ruff-0.3.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:11b5699c42f7d0b771c633d620f2cb22e727fb226273aba775a91784a9ed856c"}, + {file = "ruff-0.3.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:54e5dca3e411772b51194b3102b5f23b36961e8ede463776b289b78180df71a0"}, + {file = "ruff-0.3.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:951efb610c5844e668bbec4f71cf704f8645cf3106e13f283413969527ebfded"}, + {file = "ruff-0.3.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:09c7333b25e983aabcf6e38445252cff0b4745420fc3bda45b8fce791cc7e9ce"}, + {file = "ruff-0.3.1-py3-none-win32.whl", hash = "sha256:d937f9b99ebf346e0606c3faf43c1e297a62ad221d87ef682b5bdebe199e01f6"}, + {file = "ruff-0.3.1-py3-none-win_amd64.whl", hash = "sha256:c0318a512edc9f4e010bbaab588b5294e78c5cdc9b02c3d8ab2d77c7ae1903e3"}, + {file = "ruff-0.3.1-py3-none-win_arm64.whl", hash = "sha256:d3b60e44240f7e903e6dbae3139a65032ea4c6f2ad99b6265534ff1b83c20afa"}, + {file = "ruff-0.3.1.tar.gz", hash = "sha256:d30db97141fc2134299e6e983a6727922c9e03c031ae4883a6d69461de722ae7"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index 67977a5..6927eef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,18 +15,25 @@ dependencies = [ "pyyaml>=6.0.1", "jedi>=0.19.1", "GitPython>=3.1.41", - "llama-index<0.10.0", "chromadb>=0.4.22", "prettytable>=3.9.0", "python-iso639>=2024.2.7", "pydantic-settings>=2.2.1", "tomli>=2.0.1", "tomli-w>=1.0.0", + "llama-index<0.10.0", ] name = "repoagent" -version = "0.1.0" -description = "An Agent designed to offer an intelligent approach for generating project documentation." +version = "0.1.2" +description = "An LLM-Powered Framework for Repository-level Code Documentation Generation." readme = "README.md" +classifiers = [ + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering :: Artificial Intelligence" +] [project.urls] repository = "https://github.com/LOGIC-10/RepoAgent" @@ -47,6 +54,7 @@ test = [ [tool.pdm.build] includes = [ "repo_agent", + "assets/images/*.png" ] diff --git a/repo_agent/chat_engine.py b/repo_agent/chat_engine.py index 2586f05..380fae6 100644 --- a/repo_agent/chat_engine.py +++ b/repo_agent/chat_engine.py @@ -196,7 +196,7 @@ def get_relationship_description(referencer_content, reference_letter): else: return "" - max_tokens = setting.chat_completion.max_document_tokens + max_tokens = setting.project.max_document_tokens code_type_tell = "Class" if code_type == "ClassDef" else "Function" parameters_or_attribute = ( diff --git a/repo_agent/chat_with_repo/__init__.py b/repo_agent/chat_with_repo/__init__.py index c11272f..12b0f6e 100644 --- a/repo_agent/chat_with_repo/__init__.py +++ b/repo_agent/chat_with_repo/__init__.py @@ -1,3 +1,3 @@ # repo_agent/chat_with_repo/__init__.py -from .main import main \ No newline at end of file +from .main import main diff --git a/repo_agent/config_manager.py b/repo_agent/config_manager.py index 1c07349..b7fcb0b 100644 --- a/repo_agent/config_manager.py +++ b/repo_agent/config_manager.py @@ -20,13 +20,13 @@ def get_config_path() -> Path: os_name = os.name if os_name == 'posix': # 对于 Unix 和 macOS,使用家目录 - home_config_path = Path.home() / '.repo_agent' + home_config_path = Path.home() / '.repoagent' elif os_name == 'nt': # 对于 Windows,使用 APPDATA 目录 - home_config_path = Path(os.getenv('APPDATA')) / 'repo_agent' # type: ignore + home_config_path = Path(os.getenv('APPDATA')) / 'repoagent' # type: ignore else: # 如果操作系统检测失败,默认使用一个本地目录 - home_config_path = Path.cwd() / 'repo_agent' + home_config_path = Path.cwd() / 'repoagent' # 确保配置目录存在 home_config_path.mkdir(parents=True, exist_ok=True) diff --git a/repo_agent/doc_meta_info.py b/repo_agent/doc_meta_info.py index 996d22f..b92921f 100644 --- a/repo_agent/doc_meta_info.py +++ b/repo_agent/doc_meta_info.py @@ -80,7 +80,7 @@ class DocItemStatus(Enum): referencer_not_exist = auto() # 曾经引用他的obj被删除了,或者不再引用他了 -def need_to_generate(doc_item: DocItem, ignore_list: List = []) -> bool: +def need_to_generate(doc_item: DocItem, ignore_list: List[str] = []) -> bool: """只生成item的,文件及更高粒度都跳过。另外如果属于一个blacklist的文件也跳过""" if doc_item.item_status == DocItemStatus.doc_up_to_date: return False @@ -236,14 +236,14 @@ def find(self, recursive_file_path: list) -> Optional[DocItem]: return now @staticmethod - def check_has_task(now_item: DocItem, ignore_list: List = []): + def check_has_task(now_item: DocItem, ignore_list: List[str] = []): if need_to_generate(now_item, ignore_list=ignore_list): now_item.has_task = True for _, child in now_item.children.items(): DocItem.check_has_task(child, ignore_list) now_item.has_task = child.has_task or now_item.has_task - def print_recursive(self, indent=0, print_content=False, diff_status = False, ignore_list = []): + def print_recursive(self, indent=0, print_content=False, diff_status = False, ignore_list: List[str] = []): """递归打印repo对象""" def print_indent(indent=0): diff --git a/repo_agent/main.py b/repo_agent/main.py index 8639cfc..bea3544 100644 --- a/repo_agent/main.py +++ b/repo_agent/main.py @@ -58,7 +58,7 @@ def language_prompt(default_language): @click.group() @click.version_option(version_number) def cli(): - """An LLM-Powered Open-Source Framework for Repository-level Code Documentation Generation.""" + """An LLM-Powered Framework for Repository-level Code Documentation Generation.""" pass @@ -81,7 +81,7 @@ def configure(): ignore_list=click.prompt( "Enter files or directories to ignore, separated by commas", default=",".join( - str(path) for path in project_settings_default_instance.ignore_list + str(path) for path in [] ), ).split(","), language=language_prompt( diff --git a/repo_agent/settings.py b/repo_agent/settings.py index 6622ede..ab363b1 100644 --- a/repo_agent/settings.py +++ b/repo_agent/settings.py @@ -34,6 +34,13 @@ class ProjectSettings(BaseSettings): max_document_tokens: PositiveInt = 1024 log_level: LogLevel = LogLevel.INFO + @field_serializer("ignore_list") + def serialize_ignore_list(self, ignore_list: list[str] = []): + if ignore_list == [""]: + self.ignore_list = [] # If the ignore_list is empty, set it to an empty list + return [] + return ignore_list + @field_validator("language") @classmethod def validate_language_code(cls, v: str) -> str: diff --git a/requirements.txt b/requirements.txt index cb04185..855a0b1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -245,9 +245,9 @@ ffmpy==0.3.2 \ filelock==3.13.1 \ --hash=sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e \ --hash=sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c -flatbuffers==23.5.26 \ - --hash=sha256:9ea1144cac05ce5d86e2859f431c6cd5e66cd9c78c558317c7955fb8d4c78d89 \ - --hash=sha256:c0ff356da363087b915fde4b8b45bdda73432fc17cddb3c8157472eab1422ad1 +flatbuffers==24.3.7 \ + --hash=sha256:0895c22b9a6019ff2f4de2e5e2f7cd15914043e6e7033a94c0c6369422690f22 \ + --hash=sha256:80c4f5dcad0ee76b7e349671a0d657f2fbba927a0244f88dd3f5ed6a3694e1fc fonttools==4.49.0 \ --hash=sha256:07bc5ea02bb7bc3aa40a1eb0481ce20e8d9b9642a9536cde0218290dd6085828 \ --hash=sha256:0ba0e00620ca28d4ca11fc700806fd69144b463aa3275e1b36e56c7c09915559 \ @@ -338,12 +338,12 @@ google-auth==2.28.1 \ googleapis-common-protos==1.62.0 \ --hash=sha256:4750113612205514f9f6aa4cb00d523a94f3e8c06c5ad2fee466387dc4875f07 \ --hash=sha256:83f0ece9f94e5672cced82f592d2a5edf527a96ed1794f0bab36d5735c996277 -gradio==4.19.2 \ - --hash=sha256:6fe5815bb4dfaeed1fc74223bffd91da70a1b463158af8c5e03d01bb09068a1d \ - --hash=sha256:acab4a35f556dbc3ae637469312738d154bcb73f0b8d5f4f65e4d067ecb1e0b1 -gradio-client==0.10.1 \ - --hash=sha256:879eb56fae5d6b1603bb9375b88d1de0d034f3dac4b3afc8dbc66f36f6e54d5d \ - --hash=sha256:a0413fffdde3360e0f6aaec8b8c23d8a320049a571de2d111d85ebd295002165 +gradio==4.20.1 \ + --hash=sha256:01815047593f8d653d609ab2d9b89a7d435fd20438602de19278f8cd6acd1fb9 \ + --hash=sha256:9efb88a4e1626f4f452467e45f44c37dbd8b57f7839f71326ec169e40de5cdab +gradio-client==0.11.0 \ + --hash=sha256:18ae178f83df4a1ff53247457f6ee13c4d0dfc3b7a8bff6d9da677600f31b04e \ + --hash=sha256:638a43ccc73937790a3c68adb732da03ee7077732a0b6540a9196bacbb1f6539 greenlet==3.0.3 \ --hash=sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676 \ --hash=sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc \ @@ -434,9 +434,9 @@ httptools==0.6.1 \ httpx==0.27.0 \ --hash=sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5 \ --hash=sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5 -huggingface-hub==0.21.3 \ - --hash=sha256:26a15b604e4fc7bad37c467b76456543ec849386cbca9cd7e1e135f53e500423 \ - --hash=sha256:b183144336fdf2810a8c109822e0bb6ef1fd61c65da6fb60e8c3f658b7144016 +huggingface-hub==0.21.4 \ + --hash=sha256:df37c2c37fc6c82163cdd8a67ede261687d80d1e262526d6c0ce73b6b3630a7b \ + --hash=sha256:e1f4968c93726565a80edf6dc309763c7b546d0cfe79aa221206034d50155531 humanfriendly==10.0 \ --hash=sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477 \ --hash=sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc @@ -446,9 +446,9 @@ idna==3.6 \ importlib-metadata==6.11.0 \ --hash=sha256:1231cf92d825c9e03cfc4da076a16de6422c863558229ea0b22b675657463443 \ --hash=sha256:f0afba6205ad8f8947c7d338b5342d5db2afbfd82f9cbef7879a9539cc12eb9b -importlib-resources==6.1.2 \ - --hash=sha256:308abf8474e2dba5f867d279237cd4076482c3de7104a40b41426370e891549b \ - --hash=sha256:9a0a862501dc38b68adebc82970140c9e4209fc99601782925178f8386339938 +importlib-resources==6.1.3 \ + --hash=sha256:4c0269e3580fe2634d364b39b38b961540a7738c02cb984e98add8b4221d793d \ + --hash=sha256:56fb4525197b78544a3354ea27793952ab93f935bb4bf746b846bb1015020f2b iniconfig==2.0.0 \ --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 @@ -1023,9 +1023,9 @@ pydub==0.25.1 \ pygments==2.17.2 \ --hash=sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c \ --hash=sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367 -pyparsing==3.1.1 \ - --hash=sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb \ - --hash=sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db +pyparsing==3.1.2 \ + --hash=sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad \ + --hash=sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742 pypika==0.48.9 \ --hash=sha256:838836a61747e7c8380cd1b7ff638694b7a7335345d0f559b04b2cd832ad5378 pyproject-hooks==1.0.0 \ @@ -1217,24 +1217,24 @@ rpds-py==0.18.0 \ rsa==4.9 \ --hash=sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7 \ --hash=sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21 -ruff==0.3.0 \ - --hash=sha256:0886184ba2618d815067cf43e005388967b67ab9c80df52b32ec1152ab49f53a \ - --hash=sha256:128265876c1d703e5f5e5a4543bd8be47c73a9ba223fd3989d4aa87dd06f312f \ - --hash=sha256:19eacceb4c9406f6c41af806418a26fdb23120dfe53583df76d1401c92b7c14b \ - --hash=sha256:23dbb808e2f1d68eeadd5f655485e235c102ac6f12ad31505804edced2a5ae77 \ - --hash=sha256:2f7dbba46e2827dfcb0f0cc55fba8e96ba7c8700e0a866eb8cef7d1d66c25dcb \ - --hash=sha256:3ef655c51f41d5fa879f98e40c90072b567c666a7114fa2d9fe004dffba00932 \ - --hash=sha256:5da894a29ec018a8293d3d17c797e73b374773943e8369cfc50495573d396933 \ - --hash=sha256:755c22536d7f1889be25f2baf6fedd019d0c51d079e8417d4441159f3bcd30c2 \ - --hash=sha256:7deb528029bacf845bdbb3dbb2927d8ef9b4356a5e731b10eef171e3f0a85944 \ - --hash=sha256:9343690f95710f8cf251bee1013bf43030072b9f8d012fbed6ad702ef70d360a \ - --hash=sha256:a1f3ed501a42f60f4dedb7805fa8d4534e78b4e196f536bac926f805f0743d49 \ - --hash=sha256:b08b356d06a792e49a12074b62222f9d4ea2a11dca9da9f68163b28c71bf1dd4 \ - --hash=sha256:cc30a9053ff2f1ffb505a585797c23434d5f6c838bacfe206c0e6cf38c921a1e \ - --hash=sha256:d0d3d7ef3d4f06433d592e5f7d813314a34601e6c5be8481cccb7fa760aa243e \ - --hash=sha256:dd73fe7f4c28d317855da6a7bc4aa29a1500320818dd8f27df95f70a01b8171f \ - --hash=sha256:e1e0d4381ca88fb2b73ea0766008e703f33f460295de658f5467f6f229658c19 \ - --hash=sha256:e3a4a6d46aef0a84b74fcd201a4401ea9a6cd85614f6a9435f2d33dd8cefbf83 +ruff==0.3.1 \ + --hash=sha256:09c7333b25e983aabcf6e38445252cff0b4745420fc3bda45b8fce791cc7e9ce \ + --hash=sha256:11b5699c42f7d0b771c633d620f2cb22e727fb226273aba775a91784a9ed856c \ + --hash=sha256:434c3fc72e6311c85cd143c4c448b0e60e025a9ac1781e63ba222579a8c29200 \ + --hash=sha256:52b02bb46f1a79b0c1fa93f6495bc7e77e4ef76e6c28995b4974a20ed09c0833 \ + --hash=sha256:54e5dca3e411772b51194b3102b5f23b36961e8ede463776b289b78180df71a0 \ + --hash=sha256:5f0c21b6914c3c9a25a59497cbb1e5b6c2d8d9beecc9b8e03ee986e24eee072e \ + --hash=sha256:6b730f56ccf91225da0f06cfe421e83b8cc27b2a79393db9c3df02ed7e2bbc01 \ + --hash=sha256:6b82e3937d0d76554cd5796bc3342a7d40de44494d29ff490022d7a52c501744 \ + --hash=sha256:78a7025e6312cbba496341da5062e7cdd47d95f45c1b903e635cdeb1ba5ec2b9 \ + --hash=sha256:951efb610c5844e668bbec4f71cf704f8645cf3106e13f283413969527ebfded \ + --hash=sha256:ae7954c8f692b70e6a206087ae3988acc9295d84c550f8d90b66c62424c16771 \ + --hash=sha256:c0318a512edc9f4e010bbaab588b5294e78c5cdc9b02c3d8ab2d77c7ae1903e3 \ + --hash=sha256:c78bfa85637668f47bd82aa2ae17de2b34221ac23fea30926f6409f9e37fc927 \ + --hash=sha256:d30db97141fc2134299e6e983a6727922c9e03c031ae4883a6d69461de722ae7 \ + --hash=sha256:d3b60e44240f7e903e6dbae3139a65032ea4c6f2ad99b6265534ff1b83c20afa \ + --hash=sha256:d6abaad602d6e6daaec444cbf4d9364df0a783e49604c21499f75bb92237d4af \ + --hash=sha256:d937f9b99ebf346e0606c3faf43c1e297a62ad221d87ef682b5bdebe199e01f6 semantic-version==2.10.0 \ --hash=sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c \ --hash=sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177 From d61a377037ac419ab6931e38bef42deb4f1a102a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E9=92=A6=E9=9B=A8?= <1820851049@qq.com> Date: Mon, 11 Mar 2024 11:09:18 +0800 Subject: [PATCH 09/11] fix: add structure files automatically add to git --- pdm.lock | 50 +++++++++++++++++------------------ repo_agent/change_detector.py | 2 +- repo_agent/runner.py | 14 +++++----- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/pdm.lock b/pdm.lock index e545bb7..ba8bf17 100644 --- a/pdm.lock +++ b/pdm.lock @@ -752,7 +752,7 @@ files = [ [[package]] name = "gradio" -version = "4.20.1" +version = "4.21.0" requires_python = ">=3.8" summary = "Python library for easily interacting with trained machine learning models" groups = ["default"] @@ -761,7 +761,7 @@ dependencies = [ "altair<6.0,>=4.2.0", "fastapi", "ffmpy", - "gradio-client==0.11.0", + "gradio-client==0.12.0", "httpx>=0.24.1", "huggingface-hub>=0.19.3", "importlib-resources<7.0,>=1.3", @@ -785,13 +785,13 @@ dependencies = [ "uvicorn>=0.14.0", ] files = [ - {file = "gradio-4.20.1-py3-none-any.whl", hash = "sha256:01815047593f8d653d609ab2d9b89a7d435fd20438602de19278f8cd6acd1fb9"}, - {file = "gradio-4.20.1.tar.gz", hash = "sha256:9efb88a4e1626f4f452467e45f44c37dbd8b57f7839f71326ec169e40de5cdab"}, + {file = "gradio-4.21.0-py3-none-any.whl", hash = "sha256:521376440e4d5347f48798a15439497910041db4a522187fd8e3807a909c4a8e"}, + {file = "gradio-4.21.0.tar.gz", hash = "sha256:81acbfbf87d07d472889e14c696a3da09acc6f1abd1d7e3d7f1a963e7b727363"}, ] [[package]] name = "gradio-client" -version = "0.11.0" +version = "0.12.0" requires_python = ">=3.8" summary = "Python library for easily interacting with trained machine learning models" groups = ["default"] @@ -804,8 +804,8 @@ dependencies = [ "websockets<12.0,>=10.0", ] files = [ - {file = "gradio_client-0.11.0-py3-none-any.whl", hash = "sha256:638a43ccc73937790a3c68adb732da03ee7077732a0b6540a9196bacbb1f6539"}, - {file = "gradio_client-0.11.0.tar.gz", hash = "sha256:18ae178f83df4a1ff53247457f6ee13c4d0dfc3b7a8bff6d9da677600f31b04e"}, + {file = "gradio_client-0.12.0-py3-none-any.whl", hash = "sha256:ead1d3016cd42e9275cf62dd3227ab4472d4093da1a1c6c3c0fe6ca516e9d31f"}, + {file = "gradio_client-0.12.0.tar.gz", hash = "sha256:7a7e3406829a176153ba1a67f1e1779c2e897d775c7f70f3aeb7e7b2e1ce906b"}, ] [[package]] @@ -2596,28 +2596,28 @@ files = [ [[package]] name = "ruff" -version = "0.3.1" +version = "0.3.2" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["default", "dev"] files = [ - {file = "ruff-0.3.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6b82e3937d0d76554cd5796bc3342a7d40de44494d29ff490022d7a52c501744"}, - {file = "ruff-0.3.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ae7954c8f692b70e6a206087ae3988acc9295d84c550f8d90b66c62424c16771"}, - {file = "ruff-0.3.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b730f56ccf91225da0f06cfe421e83b8cc27b2a79393db9c3df02ed7e2bbc01"}, - {file = "ruff-0.3.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c78bfa85637668f47bd82aa2ae17de2b34221ac23fea30926f6409f9e37fc927"}, - {file = "ruff-0.3.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6abaad602d6e6daaec444cbf4d9364df0a783e49604c21499f75bb92237d4af"}, - {file = "ruff-0.3.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5f0c21b6914c3c9a25a59497cbb1e5b6c2d8d9beecc9b8e03ee986e24eee072e"}, - {file = "ruff-0.3.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:434c3fc72e6311c85cd143c4c448b0e60e025a9ac1781e63ba222579a8c29200"}, - {file = "ruff-0.3.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78a7025e6312cbba496341da5062e7cdd47d95f45c1b903e635cdeb1ba5ec2b9"}, - {file = "ruff-0.3.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52b02bb46f1a79b0c1fa93f6495bc7e77e4ef76e6c28995b4974a20ed09c0833"}, - {file = "ruff-0.3.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:11b5699c42f7d0b771c633d620f2cb22e727fb226273aba775a91784a9ed856c"}, - {file = "ruff-0.3.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:54e5dca3e411772b51194b3102b5f23b36961e8ede463776b289b78180df71a0"}, - {file = "ruff-0.3.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:951efb610c5844e668bbec4f71cf704f8645cf3106e13f283413969527ebfded"}, - {file = "ruff-0.3.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:09c7333b25e983aabcf6e38445252cff0b4745420fc3bda45b8fce791cc7e9ce"}, - {file = "ruff-0.3.1-py3-none-win32.whl", hash = "sha256:d937f9b99ebf346e0606c3faf43c1e297a62ad221d87ef682b5bdebe199e01f6"}, - {file = "ruff-0.3.1-py3-none-win_amd64.whl", hash = "sha256:c0318a512edc9f4e010bbaab588b5294e78c5cdc9b02c3d8ab2d77c7ae1903e3"}, - {file = "ruff-0.3.1-py3-none-win_arm64.whl", hash = "sha256:d3b60e44240f7e903e6dbae3139a65032ea4c6f2ad99b6265534ff1b83c20afa"}, - {file = "ruff-0.3.1.tar.gz", hash = "sha256:d30db97141fc2134299e6e983a6727922c9e03c031ae4883a6d69461de722ae7"}, + {file = "ruff-0.3.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77f2612752e25f730da7421ca5e3147b213dca4f9a0f7e0b534e9562c5441f01"}, + {file = "ruff-0.3.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9966b964b2dd1107797be9ca7195002b874424d1d5472097701ae8f43eadef5d"}, + {file = "ruff-0.3.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b83d17ff166aa0659d1e1deaf9f2f14cbe387293a906de09bc4860717eb2e2da"}, + {file = "ruff-0.3.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb875c6cc87b3703aeda85f01c9aebdce3d217aeaca3c2e52e38077383f7268a"}, + {file = "ruff-0.3.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be75e468a6a86426430373d81c041b7605137a28f7014a72d2fc749e47f572aa"}, + {file = "ruff-0.3.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:967978ac2d4506255e2f52afe70dda023fc602b283e97685c8447d036863a302"}, + {file = "ruff-0.3.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1231eacd4510f73222940727ac927bc5d07667a86b0cbe822024dd00343e77e9"}, + {file = "ruff-0.3.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c6d613b19e9a8021be2ee1d0e27710208d1603b56f47203d0abbde906929a9b"}, + {file = "ruff-0.3.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8439338a6303585d27b66b4626cbde89bb3e50fa3cae86ce52c1db7449330a7"}, + {file = "ruff-0.3.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:de8b480d8379620cbb5ea466a9e53bb467d2fb07c7eca54a4aa8576483c35d36"}, + {file = "ruff-0.3.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b74c3de9103bd35df2bb05d8b2899bf2dbe4efda6474ea9681280648ec4d237d"}, + {file = "ruff-0.3.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f380be9fc15a99765c9cf316b40b9da1f6ad2ab9639e551703e581a5e6da6745"}, + {file = "ruff-0.3.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0ac06a3759c3ab9ef86bbeca665d31ad3aa9a4b1c17684aadb7e61c10baa0df4"}, + {file = "ruff-0.3.2-py3-none-win32.whl", hash = "sha256:9bd640a8f7dd07a0b6901fcebccedadeb1a705a50350fb86b4003b805c81385a"}, + {file = "ruff-0.3.2-py3-none-win_amd64.whl", hash = "sha256:0c1bdd9920cab5707c26c8b3bf33a064a4ca7842d91a99ec0634fec68f9f4037"}, + {file = "ruff-0.3.2-py3-none-win_arm64.whl", hash = "sha256:5f65103b1d76e0d600cabd577b04179ff592064eaa451a70a81085930e907d0b"}, + {file = "ruff-0.3.2.tar.gz", hash = "sha256:fa78ec9418eb1ca3db392811df3376b46471ae93792a81af2d1cbb0e5dcb5142"}, ] [[package]] diff --git a/repo_agent/change_detector.py b/repo_agent/change_detector.py index 23fc092..bf0d7d0 100644 --- a/repo_agent/change_detector.py +++ b/repo_agent/change_detector.py @@ -213,7 +213,7 @@ def get_to_be_staged_files(self): for unstaged_file in unstaged_files: # 连接repo_path和unstaged_file以获取完整的绝对路径 - if unstaged_file.startswith(setting.project.markdown_docs_name): + if unstaged_file.startswith(setting.project.markdown_docs_name) or unstaged_file.startswith(setting.project.hierarchy_name): # abs_unstaged_file = os.path.join(self.repo_path, unstaged_file) # # # 获取相对于仓库根目录的相对路径 # # rel_unstaged_file = os.path.relpath(abs_unstaged_file, self.repo_path) diff --git a/repo_agent/runner.py b/repo_agent/runner.py index 612c8f5..f476f5c 100644 --- a/repo_agent/runner.py +++ b/repo_agent/runner.py @@ -73,9 +73,9 @@ def generate_doc_for_a_single_item(self, doc_item: DocItem): rel_file_path = doc_item.get_full_name() if not need_to_generate(doc_item, setting.project.ignore_list): - print(f"内容被忽略/文档已生成,跳过:{doc_item.get_full_name()}") + print(f"Content ignored/Document generated, skipping: {doc_item.get_full_name()}") else: - print(f" -- 正在生成文档 {Fore.LIGHTYELLOW_EX}{doc_item.item_type.name}: {doc_item.get_full_name()}{Style.RESET_ALL}") + print(f" -- Generating document {Fore.LIGHTYELLOW_EX}{doc_item.item_type.name}: {doc_item.get_full_name()}{Style.RESET_ALL}") file_handler = FileHandler(setting.project.target_repo, rel_file_path) response_message = self.chat_engine.generate_doc( doc_item=doc_item, @@ -135,7 +135,7 @@ def first_generate(self): target_dir_path=self.absolute_project_hierarchy_path ) logger.info( - f"成功生成了 {before_task_len - len(task_manager.task_dict)} 个文档" + f"Successfully generated {before_task_len - len(task_manager.task_dict)} documents." ) except BaseException as e: @@ -191,7 +191,7 @@ def to_markdown(item: DocItem, now_level: int) -> str: markdown = "" for _, child in file_item.children.items(): markdown += to_markdown(child, 2) - assert markdown != None, f"markdown内容为空,文件路径为{rel_file_path}" + assert markdown != None, f"Markdown content is empty, the file path is: {rel_file_path}" # 写入markdown内容到.md文件 file_path = os.path.join( setting.project.markdown_docs_name, @@ -256,7 +256,7 @@ def run(self): new_meta_info.load_doc_from_older_meta(self.meta_info) self.meta_info = new_meta_info # 更新自身的meta_info信息为new的信息 - self.meta_info.in_generation_process = True # 将in_generation_process设置为True,表示检测到变更后正在生成文档的过程中 + self.meta_info.in_generation_process = True # 将in_generation_process设置为True,表示检测到变更后Generating document 的过程中 # 处理任务队列 check_task_available_func = partial(need_to_generate, ignore_list=setting.project.ignore_list) @@ -301,7 +301,7 @@ def run(self): git_add_result = self.change_detector.add_unstaged_files() if len(git_add_result) > 0: - logger.info(f"已添加 {[file for file in git_add_result]} 到暂存区") + logger.info(f"Added {[file for file in git_add_result]} to the staging area.") # self.git_commit(f"Update documentation for {file_handler.file_path}") # 提交变更 @@ -339,7 +339,7 @@ def add_new_item(self, file_handler, json_data): # 将新的项写入json文件 with open(self.project_manager.project_hierarchy, "w", encoding="utf-8") as f: json.dump(json_data, f, indent=4, ensure_ascii=False) - logger.info(f"已将新增文件 {file_handler.file_path} 的结构信息写入json文件。") + logger.info(f"The structural information of the newly added file {file_handler.file_path} has been written into a JSON file.") # 将变更部分的json文件内容转换成markdown内容 markdown = file_handler.convert_to_markdown_file( file_path=file_handler.file_path From c971121210726ad17b1b4497e9655cd9be8e342a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E9=92=A6=E9=9B=A8?= <1820851049@qq.com> Date: Mon, 11 Mar 2024 11:21:58 +0800 Subject: [PATCH 10/11] docs: update readme for new precommit modification --- README.md | 13 +++++++------ README_CN.md | 10 ++++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 777faa6..260ec1a 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ If you're looking to contribute or set up a development environment: ### Configuring RepoAgent -You can use RepoAgent with OpenAI API for the moment. +Before configuring specific parameters for RepoAgent, please ensure that the OpenAI API is configured as an environment variable in the command line: ```sh export OPENAI_API_KEY=YOUR_API_KEY # on Linux/Mac @@ -102,7 +102,7 @@ set OPENAI_API_KEY=YOUR_API_KEY # on Windows $Env:OPENAI_API_KEY = "YOUR_API_KEY" # on Windows (PowerShell) ``` -Use `repoagent configure` if you need to running parameters. +Use `repoagent configure` if you need to modify the running parameters. ```sh Enter the path to target repository: @@ -132,7 +132,7 @@ The run command supports the following optional flags (if set, will override con - `-t`, `--temperature` FLOAT: Sets the generation temperature for the model. Lower values make the model more deterministic. Default: `0.2` - `-r`, `--request-timeout` INTEGER: Defines the timeout in seconds for the API request. Default: `60` - `-b`, `--base-url` TEXT: The base URL for the API calls. Default: `https://api.openai.com/v1` -- `-tp`, `--target-repo-path` PATH: The file system path to the target repository. Used as the root for documentation generation. Default: `/home/test/arno` +- `-tp`, `--target-repo-path` PATH: The file system path to the target repository. Used as the root for documentation generation. Default: `path/to/your/target/repository` - `-hp`, `--hierarchy-path` TEXT: The name or path for the project hierarchy file, used to organize documentation structure. Default: `.project_doc_record` - `-mdp`, `--markdown-docs-path` TEXT: The folder path where Markdown documentation will be stored or generated. Default: `markdown_docs` - `-i`, `--ignore-list` TEXT: A list of files or directories to ignore during documentation generation, separated by commas. @@ -143,9 +143,9 @@ The run command supports the following optional flags (if set, will override con You can also try the following feature ```sh -repo_agent clean # Remove repoagent-related cache -repo_agent print-hierarchy # Print how repo-agent parse the target repo -repo_agent diff # Check what docs will be updated/generated based on current code change +repoagent clean # Remove repoagent-related cache +repoagent print-hierarchy # Print how repo-agent parse the target repo +repoagent diff # Check what docs will be updated/generated based on current code change ``` If it's your first time generating documentation for the target repository, RepoAgent will automatically create a JSON file maintaining the global structure information and a folder named Markdown_Docs in the root directory of the target repository for storing documents. @@ -176,6 +176,7 @@ repos: name: RepoAgent entry: repoagent language: system + pass_filenames: false # prevent from passing filenames to the hook # You can specify the file types that trigger the hook, but currently only python is supported. types: [python] ``` diff --git a/README_CN.md b/README_CN.md index e9dba11..27f5dce 100644 --- a/README_CN.md +++ b/README_CN.md @@ -93,10 +93,11 @@ pip install repoagent ### 配置RepoAgent -当前可以可以配置 OpenAI API 来使用 RepoAgent +在配置RepoAgent具体参数之前,请先确保已经在命令行配置 OpenAI API 作为环境变量: ```sh export OPENAI_API_KEY=YOUR_API_KEY # on Linux/Mac + set OPENAI_API_KEY=YOUR_API_KEY # on Windows $Env:OPENAI_API_KEY = "YOUR_API_KEY" # on Windows (PowerShell) ``` @@ -124,7 +125,7 @@ run 命令支持以下可选标志(如果设置,将覆盖配置默认值) - `-t`, `--temperature` FLOAT:设置模型的生成温度。较低的值使模型更确定性。默认值:`0.2` - `-r`, `--request-timeout` INTEGER:定义 API 请求的超时时间(秒)。默认值:`60` - `-b`, `--base-url` TEXT:API 调用的基础 URL。默认值:`https://api.openai.com/v1` -- `-tp`, `--target-repo-path` PATH:目标仓库的文件系统路径。用作文档生成的根路径。默认值:`/home/test/arno` +- `-tp`, `--target-repo-path` PATH:目标仓库的文件系统路径。用作文档生成的根路径。默认值:`path/to/your/target/repository` - `-hp`, `--hierarchy-path` TEXT:项目层级文件的名称或路径,用于组织文档结构。默认值:`.project_doc_record` - `-mdp`, `--markdown-docs-path` TEXT:Markdown 文档将被存储或生成的文件夹路径。默认值:`markdown_docs` - `-i`, `--ignore-list` TEXT:在文档生成过程中要忽略的文件或目录列表,用逗号分隔。 @@ -135,11 +136,11 @@ run 命令支持以下可选标志(如果设置,将覆盖配置默认值) ```sh repoagent clean # 此命令将删除与repoagent相关的缓存 -repoagent print # 此命令将打印repo-agent如何解析目标仓库 +repoagent print-hierarchy # 此命令将打印repoagent解析出的目标仓库 repoagent diff # 此命令将检查基于当前代码更改将更新/生成哪些文档 ``` -如果您是第一次对目标仓库生成文档,此时RepoAgent会自动生成一个维护全局结构信息的json文件,并在目标仓库根目录下创建一个名为Markdown_Docs的文件夹,用于存放文档。 +如果您是第一次对目标仓库生成文档,此时RepoAgent会自动生成一个维护全局结构信息的json文件,并在目标仓库根目录下创建一个文件夹用于存放文档。 全局结构信息json文件和文档文件夹的路径都可以在`config.yml`中进行配置。 当您首次完成对目标仓库生成全局文档后,或您clone下来的项目已经包含了全局文档信息后,就可以通过**pre-commit**配置目标仓库**hook**和团队一起无缝自动维护一个项目内部文档了! @@ -165,6 +166,7 @@ repos: name: RepoAgent entry: repoagent language: system + pass_filenames: false # 阻止pre commit传入文件名作为参数 # 可以指定钩子触发的文件类型,但是目前只支持python types: [python] ``` From 2f9b56136812053173d45e6d116eb83e54788dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E9=92=A6=E9=9B=A8?= <1820851049@qq.com> Date: Mon, 11 Mar 2024 11:26:14 +0800 Subject: [PATCH 11/11] docs: update readme_cn for new running settings. --- README_CN.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README_CN.md b/README_CN.md index 27f5dce..6343afb 100644 --- a/README_CN.md +++ b/README_CN.md @@ -119,6 +119,13 @@ Enter the request timeout (seconds) [60.0]: Enter the base URL [https://api.openai.com/v1]: ``` +## 运行RepoAgent + +进入RepoAgent根目录并在终端尝试以下命令: +```sh +repoagent run # 这条命令会生成文档或自动更新文档 (pre-commit-hook 会自动调用它) +``` + run 命令支持以下可选标志(如果设置,将覆盖配置默认值): - `-m`, `--model` TEXT:指定用于完成的模型。默认值:`gpt-3.5-turbo` @@ -145,7 +152,7 @@ repoagent diff # 此命令将检查基于当前代码更改将更新/生成哪 当您首次完成对目标仓库生成全局文档后,或您clone下来的项目已经包含了全局文档信息后,就可以通过**pre-commit**配置目标仓库**hook**和团队一起无缝自动维护一个项目内部文档了! -## 配置目标仓库 +### 配置目标仓库 RepoAgent目前支持对项目的文档生成和自动维护,因此需要对目标仓库进行一定的配置。