diff --git a/.gitignore b/.gitignore index 6bc9886..17cc079 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ **/__pycache__ knowledge_base logs +llm_models embedding_models jupyter_work model_config.py diff --git a/README.md b/README.md index 8853fef..32973d8 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,11 @@

-DevOps-ChatBot是由蚂蚁CodeFuse团队开发的开源AI智能助手,致力于简化和优化软件开发生命周期中的各个环节。该项目结合了Multi-Agent的协同调度机制,并集成了丰富的工具库、代码库、知识库和沙盒环境,使得LLM模型能够在DevOps领域内有效执行和处理复杂任务。 +CodeFuse-ChatBot是由蚂蚁CodeFuse团队开发的开源AI智能助手,致力于简化和优化软件开发生命周期中的各个环节。该项目结合了Multi-Agent的协同调度机制,并集成了丰富的工具库、代码库、知识库和沙盒环境,使得LLM模型能够在DevOps领域内有效执行和处理复杂任务。 ## 🔔 更新 -- [2024.01.29] 开放可配置化的multi-agent框架:coagent,详情见[使用说明](sources/readme_docs/coagent/coagent.md) +- [2024.01.29] 开放可配置化的multi-agent框架:codefuse-muAgent,详情见[使用说明](sources/readme_docs/coagent/coagent.md) - [2023.12.26] 基于FastChat接入开源私有化大模型和大模型接口的能力开放 - [2023.12.14] 量子位公众号专题报道:[文章链接](https://mp.weixin.qq.com/s/MuPfayYTk9ZW6lcqgMpqKA) - [2023.12.01] Multi-Agent和代码库检索功能开放 @@ -96,10 +96,10 @@ DevOps-ChatBot是由蚂蚁CodeFuse团队开发的开源AI智能助手,致力 ## 🚀 快速使用 -### coagent-py -完整文档见:[coagent](sources/readme_docs/coagent/coagent.md) +### muagent-py +完整文档见:[CodeFuse-muAgent](sources/readme_docs/coagent/coagent.md) ``` -pip install coagent +pip install codefuse-muagent ``` ### 使用ChatBot @@ -128,7 +128,7 @@ pip install -r requirements.txt # 完成server_config.py配置后,可一键启动 cd examples bash start.sh -# 开始在页面进行配置即可 +# 开始在页面进行相关配置,然后打开`启动对话服务`即可 ```
图片 diff --git a/README_en.md b/README_en.md index b5a97bb..b4ea3e7 100644 --- a/README_en.md +++ b/README_en.md @@ -17,7 +17,7 @@ This project is an open-source AI intelligent assistant, specifically designed f ## 🔔 Updates -- [2024.01.29] A configurational multi-agent framework, CoAgent, has been open-sourced. For more details, please refer to [coagent](sources/readme_docs/coagent/coagent-en.md) +- [2024.01.29] A configurational multi-agent framework, codefuse-muagent, has been open-sourced. For more details, please refer to [codefuse-muagent](sources/readme_docs/coagent/coagent-en.md) - [2023.12.26] Opening the capability to integrate with open-source private large models and large model interfaces based on FastChat - [2023.12.01] Release of Multi-Agent and codebase retrieval functionalities. - [2023.11.15] Addition of Q&A enhancement mode based on the local codebase. @@ -34,7 +34,7 @@ This project is an open-source AI intelligent assistant, specifically designed f 💡 The aim of this project is to construct an AI intelligent assistant for the entire lifecycle of software development, covering design, coding, testing, deployment, and operations, through Retrieval Augmented Generation (RAG), Tool Learning, and sandbox environments. It transitions gradually from the traditional development and operations mode of querying information from various sources and operating on standalone, disparate platforms to an intelligent development and operations mode based on large-model Q&A, changing people's development and operations habits. -- **🧠 Intelligent Scheduling Core:** Constructed a well-integrated scheduling core system that supports multi-mode one-click configuration, simplifying the operational process. [coagent](sources/readme_docs/coagent/coagent-en.md) +- **🧠 Intelligent Scheduling Core:** Constructed a well-integrated scheduling core system that supports multi-mode one-click configuration, simplifying the operational process. [codefuse-muagent](sources/readme_docs/coagent/coagent-en.md) - **💻 Comprehensive Code Repository Analysis:** Achieved in-depth understanding at the repository level and coding and generation at the project file level, enhancing development efficiency. - **📄 Enhanced Document Analysis:** Integrated document knowledge bases with knowledge graphs, providing deeper support for document analysis through enhanced retrieval and reasoning. - **🔧 Industry-Specific Knowledge:** Tailored a specialized knowledge base for the DevOps domain, supporting the self-service one-click construction of industry-specific knowledge bases for convenience and practicality. @@ -83,10 +83,10 @@ If you need to integrate a specific model, please inform us of your requirements ## 🚀 Quick Start -### coagent-py -More Detail see:[coagent](sources/readme_docs/coagent/coagent-en.md) +### muagent-py +More Detail see:[codefuse-muagent](sources/readme_docs/coagent/coagent-en.md) ``` -pip install coagent +pip install codefuse-muagent ``` ### ChatBot-UI @@ -108,51 +108,12 @@ cd Codefuse-ChatBot pip install -r requirements.txt ``` -2. Preparation of Sandbox Environment -- Windows Docker installation: -[Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/) supports 64-bit versions of Windows 10 Pro, with Hyper-V enabled (not required for versions v1903 and above), or 64-bit versions of Windows 10 Home v1903 and above. - - - [Comprehensive Detailed Windows 10 Docker Installation Tutorial](https://zhuanlan.zhihu.com/p/441965046) - - [Docker: From Beginner to Practitioner](https://yeasy.gitbook.io/docker_practice/install/windows) - - [Handling Docker Desktop requires the Server service to be enabled](https://blog.csdn.net/sunhy_csdn/article/details/106526991) - - [Install wsl or wait for error prompt](https://learn.microsoft.com/en-us/windows/wsl/install) - -- Linux Docker Installation: -Linux installation is relatively simple, please search Baidu/Google for installation instructions. - -- Mac Docker Installation - - [Docker: From Beginner to Practitioner](https://yeasy.gitbook.io/docker_practice/install/mac) - -```bash -# Build images for the sandbox environment, see above for notebook version issues -bash docker_build.sh -``` - -3. Model Download (Optional) - -If you need to use open-source LLM and Embed - -ding models, you can download them from HuggingFace. -Here, we use THUDM/chatglm2-6b and text2vec-base-chinese as examples: - -``` -# install git-lfs -git lfs install - -# install LLM-model -git lfs clone https://huggingface.co/THUDM/chatglm2-6b - -# install Embedding-model -git lfs clone https://huggingface.co/shibing624/text2vec-base-chinese -``` - - -4. Start the Service +2. Start the Service ```bash # After configuring server_config.py, you can start with just one click. cd examples bash start.sh -# you can config your llm model and embedding model +# you can config your llm model and embedding model, then choose the "启动对话服务" ```
图片 diff --git a/coagent/__init__.py b/coagent/__init__.py deleted file mode 100644 index 67c87c2..0000000 --- a/coagent/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: __init__.py.py -@time: 2023/11/9 下午4:01 -@desc: -''' \ No newline at end of file diff --git a/coagent/base_configs/__init__.py b/coagent/base_configs/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/coagent/base_configs/env_config.py b/coagent/base_configs/env_config.py deleted file mode 100644 index 8005dcd..0000000 --- a/coagent/base_configs/env_config.py +++ /dev/null @@ -1,93 +0,0 @@ -import os -import platform -from loguru import logger - -system_name = platform.system() -executable_path = os.getcwd() - -# 日志存储路径 -LOG_PATH = os.environ.get("LOG_PATH", None) or os.path.join(executable_path, "logs") - -# # 知识库默认存储路径 -# SOURCE_PATH = os.environ.get("SOURCE_PATH", None) or os.path.join(executable_path, "sources") - -# 知识库默认存储路径 -KB_ROOT_PATH = os.environ.get("KB_ROOT_PATH", None) or os.path.join(executable_path, "knowledge_base") - -# 代码库默认存储路径 -CB_ROOT_PATH = os.environ.get("CB_ROOT_PATH", None) or os.path.join(executable_path, "code_base") - -# # nltk 模型存储路径 -# NLTK_DATA_PATH = os.environ.get("NLTK_DATA_PATH", None) or os.path.join(executable_path, "nltk_data") - -# 代码存储路径 -JUPYTER_WORK_PATH = os.environ.get("JUPYTER_WORK_PATH", None) or os.path.join(executable_path, "jupyter_work") - -# WEB_CRAWL存储路径 -WEB_CRAWL_PATH = os.environ.get("WEB_CRAWL_PATH", None) or os.path.join(executable_path, "knowledge_base") - -# NEBULA_DATA存储路径 -NEBULA_PATH = os.environ.get("NEBULA_PATH", None) or os.path.join(executable_path, "data/nebula_data") - -# CHROMA 存储路径 -CHROMA_PERSISTENT_PATH = os.environ.get("CHROMA_PERSISTENT_PATH", None) or os.path.join(executable_path, "data/chroma_data") - -for _path in [LOG_PATH, KB_ROOT_PATH, CB_ROOT_PATH, JUPYTER_WORK_PATH, WEB_CRAWL_PATH, NEBULA_PATH, CHROMA_PERSISTENT_PATH]: - if not os.path.exists(_path) and int(os.environ.get("do_create_dir", True)): - os.makedirs(_path, exist_ok=True) - -# 数据库默认存储路径。 -# 如果使用sqlite,可以直接修改DB_ROOT_PATH;如果使用其它数据库,请直接修改SQLALCHEMY_DATABASE_URI。 -DB_ROOT_PATH = os.path.join(KB_ROOT_PATH, "info.db") -SQLALCHEMY_DATABASE_URI = f"sqlite:///{DB_ROOT_PATH}" - -kbs_config = { - "faiss": { - },} - - -# GENERAL SERVER CONFIG -DEFAULT_BIND_HOST = os.environ.get("DEFAULT_BIND_HOST", None) or "127.0.0.1" - -# NEBULA SERVER CONFIG -NEBULA_HOST = DEFAULT_BIND_HOST -NEBULA_PORT = 9669 -NEBULA_STORAGED_PORT = 9779 -NEBULA_USER = 'root' -NEBULA_PASSWORD = '' -NEBULA_GRAPH_SERVER = { - "host": DEFAULT_BIND_HOST, - "port": NEBULA_PORT, - "docker_port": NEBULA_PORT -} - -# CHROMA CONFIG -# CHROMA_PERSISTENT_PATH = '/home/user/chatbot/data/chroma_data' -# CHROMA_PERSISTENT_PATH = '/Users/bingxu/Desktop/工作/大模型/chatbot/codefuse-chatbot-antcode/data/chroma_data' - - -# 默认向量库类型。可选:faiss, milvus, pg. -DEFAULT_VS_TYPE = os.environ.get("DEFAULT_VS_TYPE") or "faiss" - -# 缓存向量库数量 -CACHED_VS_NUM = os.environ.get("CACHED_VS_NUM") or 1 - -# 知识库中单段文本长度 -CHUNK_SIZE = os.environ.get("CHUNK_SIZE") or 500 - -# 知识库中相邻文本重合长度 -OVERLAP_SIZE = os.environ.get("OVERLAP_SIZE") or 50 - -# 知识库匹配向量数量 -VECTOR_SEARCH_TOP_K = os.environ.get("VECTOR_SEARCH_TOP_K") or 5 - -# 知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右 -# Mac 可能存在无法使用normalized_L2的问题,因此调整SCORE_THRESHOLD至 0~1100 -FAISS_NORMALIZE_L2 = True if system_name in ["Linux", "Windows"] else False -SCORE_THRESHOLD = 1 if system_name in ["Linux", "Windows"] else 1100 - -# 搜索引擎匹配结题数量 -SEARCH_ENGINE_TOP_K = os.environ.get("SEARCH_ENGINE_TOP_K") or 5 - -# 代码引擎匹配结题数量 -CODE_SEARCH_TOP_K = os.environ.get("CODE_SEARCH_TOP_K") or 1 \ No newline at end of file diff --git a/coagent/chat/__init__.py b/coagent/chat/__init__.py deleted file mode 100644 index adb51e6..0000000 --- a/coagent/chat/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -from .base_chat import Chat -from .knowledge_chat import KnowledgeChat -from .llm_chat import LLMChat -from .search_chat import SearchChat -from .code_chat import CodeChat -from .agent_chat import AgentChat - - -__all__ = [ - "Chat", "KnowledgeChat", "LLMChat", "SearchChat", "CodeChat", "AgentChat" -] diff --git a/coagent/chat/agent_chat.py b/coagent/chat/agent_chat.py deleted file mode 100644 index 62def33..0000000 --- a/coagent/chat/agent_chat.py +++ /dev/null @@ -1,348 +0,0 @@ -from fastapi import Body, Request -from fastapi.responses import StreamingResponse -from typing import List, Union, Dict -from loguru import logger -import importlib -import copy -import json -import os -from pathlib import Path - -# from configs.model_config import ( -# llm_model_dict, LLM_MODEL, PROMPT_TEMPLATE, -# VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD) - -from coagent.tools import ( - toLangchainTools, - TOOL_DICT, TOOL_SETS -) - -from coagent.connector.phase import BasePhase -from coagent.connector.schema import Message -from coagent.connector.schema import Memory -from coagent.chat.utils import History, wrap_done -from coagent.llm_models.llm_config import LLMConfig, EmbedConfig -from coagent.connector.configs import PHASE_CONFIGS, AGETN_CONFIGS, CHAIN_CONFIGS - -PHASE_MODULE = importlib.import_module("coagent.connector.phase") - - - -class AgentChat: - - def __init__( - self, - engine_name: str = "", - top_k: int = 1, - stream: bool = False, - ) -> None: - self.top_k = top_k - self.stream = stream - self.chatPhase_dict: Dict[str, BasePhase] = {} - - def chat( - self, - query: str = Body(..., description="用户输入", examples=["hello"]), - phase_name: str = Body(..., description="执行场景名称", examples=["chatPhase"]), - chain_name: str = Body(..., description="执行链的名称", examples=["chatChain"]), - history: List[History] = Body( - [], description="历史对话", - examples=[[{"role": "user", "content": "我们来玩成语接龙,我先来,生龙活虎"}]] - ), - doc_engine_name: str = Body(..., description="知识库名称", examples=["samples"]), - search_engine_name: str = Body(..., description="搜索引擎名称", examples=["duckduckgo"]), - code_engine_name: str = Body(..., description="代码引擎名称", examples=["samples"]), - top_k: int = Body(5, description="匹配向量数"), - score_threshold: float = Body(1, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1), - stream: bool = Body(False, description="流式输出"), - local_doc_url: bool = Body(False, description="知识文件返回本地路径(true)或URL(false)"), - choose_tools: List[str] = Body([], description="选择tool的集合"), - do_search: bool = Body(False, description="是否进行搜索"), - do_doc_retrieval: bool = Body(False, description="是否进行知识库检索"), - do_code_retrieval: bool = Body(False, description="是否执行代码检索"), - do_tool_retrieval: bool = Body(False, description="是否执行工具检索"), - custom_phase_configs: dict = Body({}, description="自定义phase配置"), - custom_chain_configs: dict = Body({}, description="自定义chain配置"), - custom_role_configs: dict = Body({}, description="自定义role配置"), - history_node_list: List = Body([], description="代码历史相关节点"), - isDetailed: bool = Body(False, description="是否输出完整的agent相关内容"), - upload_file: Union[str, Path, bytes] = "", - kb_root_path: str = Body("", description="知识库存储路径"), - jupyter_work_path: str = Body("", description="sandbox执行环境"), - sandbox_server: str = Body({}, description="代码历史相关节点"), - api_key: str = Body(os.environ.get("OPENAI_API_KEY"), description=""), - api_base_url: str = Body(os.environ.get("API_BASE_URL"),), - embed_model: str = Body("", description="向量模型"), - embed_model_path: str = Body("", description="向量模型路径"), - model_device: str = Body("", description="模型加载设备"), - embed_engine: str = Body("", description="向量模型类型"), - model_name: str = Body("", description="llm模型名称"), - temperature: float = Body(0.2, description=""), - **kargs - ) -> Message: - - # update configs - phase_configs, chain_configs, agent_configs = self.update_configs( - custom_phase_configs, custom_chain_configs, custom_role_configs) - params = locals() - params.pop("self") - embed_config: EmbedConfig = EmbedConfig(**params) - llm_config: LLMConfig = LLMConfig(**params) - - logger.info('phase_configs={}'.format(phase_configs)) - logger.info('chain_configs={}'.format(chain_configs)) - logger.info('agent_configs={}'.format(agent_configs)) - logger.info('phase_name') - logger.info('chain_name') - - # choose tools - tools = toLangchainTools([TOOL_DICT[i] for i in choose_tools if i in TOOL_DICT]) - - if upload_file: - upload_file_name = upload_file if upload_file and isinstance(upload_file, str) else upload_file.name - for _filename_idx in range(len(upload_file_name), 0, -1): - if upload_file_name[:_filename_idx] in query: - query = query.replace(upload_file_name[:_filename_idx], upload_file_name) - break - - input_message = Message( - role_content=query, - role_type="user", - role_name="human", - input_query=query, - origin_query=query, - phase_name=phase_name, - chain_name=chain_name, - do_search=do_search, - do_doc_retrieval=do_doc_retrieval, - do_code_retrieval=do_code_retrieval, - do_tool_retrieval=do_tool_retrieval, - doc_engine_name=doc_engine_name, search_engine_name=search_engine_name, - code_engine_name=code_engine_name, - score_threshold=score_threshold, top_k=top_k, - history_node_list=history_node_list, - tools=tools - ) - # history memory mangemant - history = Memory(messages=[ - Message(role_name=i["role"], role_type=i["role"], role_content=i["content"]) - for i in history - ]) - # start to execute - phase_class = getattr(PHASE_MODULE, phase_configs[input_message.phase_name]["phase_type"]) - # TODO 需要把相关信息补充上去 - phase = phase_class(input_message.phase_name, - task = input_message.task, - base_phase_config = phase_configs, - base_chain_config = chain_configs, - base_role_config = agent_configs, - phase_config = None, - kb_root_path = kb_root_path, - jupyter_work_path = jupyter_work_path, - sandbox_server = sandbox_server, - embed_config = embed_config, - llm_config = llm_config, - ) - output_message, local_memory = phase.step(input_message, history) - - def chat_iterator(message: Message, local_memory: Memory, isDetailed=False): - step_content = local_memory.to_str_messages(content_key='step_content', filter_roles=["user"]) - final_content = message.role_content - logger.debug(f"{step_content}") - result = { - "answer": "", - "db_docs": [str(doc) for doc in message.db_docs], - "search_docs": [str(doc) for doc in message.search_docs], - "code_docs": [str(doc) for doc in message.code_docs], - "related_nodes": [doc.get_related_node() for idx, doc in enumerate(message.code_docs) if idx==0], - "figures": message.figures, - "step_content": step_content, - "final_content": final_content, - } - - - related_nodes, has_nodes = [], [ ] - for nodes in result["related_nodes"]: - for node in nodes: - if node not in has_nodes: - related_nodes.append(node) - result["related_nodes"] = related_nodes - - # logger.debug(f"{result['figures'].keys()}, isDetailed: {isDetailed}") - message_str = step_content - if self.stream: - for token in message_str: - result["answer"] = token - yield json.dumps(result, ensure_ascii=False) - else: - for token in message_str: - result["answer"] += token - yield json.dumps(result, ensure_ascii=False) - - return StreamingResponse(chat_iterator(output_message, local_memory, isDetailed), media_type="text/event-stream") - - - def achat( - self, - query: str = Body(..., description="用户输入", examples=["hello"]), - phase_name: str = Body(..., description="执行场景名称", examples=["chatPhase"]), - chain_name: str = Body(..., description="执行链的名称", examples=["chatChain"]), - history: List[History] = Body( - [], description="历史对话", - examples=[[{"role": "user", "content": "我们来玩成语接龙,我先来,生龙活虎"}]] - ), - doc_engine_name: str = Body(..., description="知识库名称", examples=["samples"]), - search_engine_name: str = Body(..., description="搜索引擎名称", examples=["duckduckgo"]), - code_engine_name: str = Body(..., description="代码引擎名称", examples=["samples"]), - cb_search_type: str = Body(..., description="代码查询模式", examples=["tag"]), - top_k: int = Body(5, description="匹配向量数"), - score_threshold: float = Body(1, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1), - stream: bool = Body(False, description="流式输出"), - local_doc_url: bool = Body(False, description="知识文件返回本地路径(true)或URL(false)"), - choose_tools: List[str] = Body([], description="选择tool的集合"), - do_search: bool = Body(False, description="是否进行搜索"), - do_doc_retrieval: bool = Body(False, description="是否进行知识库检索"), - do_code_retrieval: bool = Body(False, description="是否执行代码检索"), - do_tool_retrieval: bool = Body(False, description="是否执行工具检索"), - custom_phase_configs: dict = Body({}, description="自定义phase配置"), - custom_chain_configs: dict = Body({}, description="自定义chain配置"), - custom_role_configs: dict = Body({}, description="自定义role配置"), - history_node_list: List = Body([], description="代码历史相关节点"), - isDetailed: bool = Body(False, description="是否输出完整的agent相关内容"), - upload_file: Union[str, Path, bytes] = "", - kb_root_path: str = Body("", description="知识库存储路径"), - jupyter_work_path: str = Body("", description="sandbox执行环境"), - sandbox_server: str = Body({}, description="代码历史相关节点"), - api_key: str = Body(os.environ["OPENAI_API_KEY"], description=""), - api_base_url: str = Body(os.environ.get("API_BASE_URL"),), - embed_model: str = Body("", description="向量模型"), - embed_model_path: str = Body("", description="向量模型路径"), - model_device: str = Body("", description="模型加载设备"), - embed_engine: str = Body("", description="向量模型类型"), - model_name: str = Body("", description="llm模型名称"), - temperature: float = Body(0.2, description=""), - **kargs - ) -> Message: - - # update configs - phase_configs, chain_configs, agent_configs = self.update_configs( - custom_phase_configs, custom_chain_configs, custom_role_configs) - - # - params = locals() - params.pop("self") - embed_config: EmbedConfig = EmbedConfig(**params) - llm_config: LLMConfig = LLMConfig(**params) - - # choose tools - tools = toLangchainTools([TOOL_DICT[i] for i in choose_tools if i in TOOL_DICT]) - - if upload_file: - upload_file_name = upload_file if upload_file and isinstance(upload_file, str) else upload_file.name - for _filename_idx in range(len(upload_file_name), 0, -1): - if upload_file_name[:_filename_idx] in query: - query = query.replace(upload_file_name[:_filename_idx], upload_file_name) - break - - input_message = Message( - role_content=query, - role_type="user", - role_name="human", - input_query=query, - origin_query=query, - phase_name=phase_name, - chain_name=chain_name, - do_search=do_search, - do_doc_retrieval=do_doc_retrieval, - do_code_retrieval=do_code_retrieval, - do_tool_retrieval=do_tool_retrieval, - doc_engine_name=doc_engine_name, - search_engine_name=search_engine_name, - code_engine_name=code_engine_name, - cb_search_type=cb_search_type, - score_threshold=score_threshold, top_k=top_k, - history_node_list=history_node_list, - tools=tools - ) - # history memory mangemant - history = Memory(messages=[ - Message(role_name=i["role"], role_type=i["role"], role_content=i["content"]) - for i in history - ]) - # start to execute - if phase_configs[input_message.phase_name]["phase_type"] not in self.chatPhase_dict: - phase_class = getattr(PHASE_MODULE, phase_configs[input_message.phase_name]["phase_type"]) - phase = phase_class(input_message.phase_name, - task = input_message.task, - base_phase_config = phase_configs, - base_chain_config = chain_configs, - base_role_config = agent_configs, - phase_config = None, - kb_root_path = kb_root_path, - jupyter_work_path = jupyter_work_path, - sandbox_server = sandbox_server, - embed_config = embed_config, - llm_config = llm_config, - ) - self.chatPhase_dict[phase_configs[input_message.phase_name]["phase_type"]] = phase - else: - phase = self.chatPhase_dict[phase_configs[input_message.phase_name]["phase_type"]] - - def chat_iterator(message: Message, local_memory: Memory, isDetailed=False): - step_content = local_memory.to_str_messages(content_key='step_content', filter_roles=["human"]) - step_content = "\n\n".join([f"{v}" for parsed_output in local_memory.get_parserd_output_list()[1:] for k, v in parsed_output.items() if k not in ["Action Status"]]) - final_content = message.role_content - result = { - "answer": "", - "db_docs": [str(doc) for doc in message.db_docs], - "search_docs": [str(doc) for doc in message.search_docs], - "code_docs": [str(doc) for doc in message.code_docs], - "related_nodes": [doc.get_related_node() for idx, doc in enumerate(message.code_docs) if idx==0], - "figures": message.figures, - "step_content": step_content or final_content, - "final_content": final_content, - } - - related_nodes, has_nodes = [], [ ] - for nodes in result["related_nodes"]: - for node in nodes: - if node not in has_nodes: - related_nodes.append(node) - result["related_nodes"] = related_nodes - - # logger.debug(f"{result['figures'].keys()}, isDetailed: {isDetailed}") - message_str = step_content - if self.stream: - for token in message_str: - result["answer"] = token - yield json.dumps(result, ensure_ascii=False) - else: - for token in message_str: - result["answer"] += token - yield json.dumps(result, ensure_ascii=False) - - - for output_message, local_memory in phase.astep(input_message, history): - - # logger.debug(f"output_message: {output_message}") - # output_message = Message(**output_message) - # local_memory = Memory(**local_memory) - for result in chat_iterator(output_message, local_memory, isDetailed): - yield result - - - def _chat(self, ): - pass - - def update_configs(self, custom_phase_configs, custom_chain_configs, custom_role_configs): - '''update phase/chain/agent configs''' - phase_configs = copy.deepcopy(PHASE_CONFIGS) - phase_configs.update(custom_phase_configs) - chain_configs = copy.deepcopy(CHAIN_CONFIGS) - chain_configs.update(custom_chain_configs) - agent_configs = copy.deepcopy(AGETN_CONFIGS) - agent_configs.update(custom_role_configs) - # phase_configs = load_phase_configs(new_phase_configs) - # chian_configs = load_chain_configs(new_chain_configs) - # agent_configs = load_role_configs(new_agent_configs) - return phase_configs, chain_configs, agent_configs \ No newline at end of file diff --git a/coagent/chat/base_chat.py b/coagent/chat/base_chat.py deleted file mode 100644 index 2fe0943..0000000 --- a/coagent/chat/base_chat.py +++ /dev/null @@ -1,173 +0,0 @@ -from fastapi import Body, Request -from fastapi.responses import StreamingResponse -import asyncio, json, os -from typing import List, AsyncIterable - -from langchain import LLMChain -from langchain.callbacks import AsyncIteratorCallbackHandler -from langchain.prompts.chat import ChatPromptTemplate - -from coagent.llm_models import getChatModelFromConfig -from coagent.chat.utils import History, wrap_done -from coagent.llm_models.llm_config import LLMConfig, EmbedConfig -# from configs.model_config import (llm_model_dict, LLM_MODEL, VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD) -from coagent.utils import BaseResponse -from loguru import logger - - - -class Chat: - def __init__( - self, - engine_name: str = "", - top_k: int = 1, - stream: bool = False, - ) -> None: - self.engine_name = engine_name - self.top_k = top_k - self.stream = stream - - def check_service_status(self, ) -> BaseResponse: - return BaseResponse(code=200, msg=f"okok") - - def chat( - self, - query: str = Body(..., description="用户输入", examples=["hello"]), - history: List[History] = Body( - [], description="历史对话", - examples=[[{"role": "user", "content": "我们来玩成语接龙,我先来,生龙活虎"}]] - ), - engine_name: str = Body(..., description="知识库名称", examples=["samples"]), - top_k: int = Body(5, description="匹配向量数"), - score_threshold: float = Body(1, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1), - stream: bool = Body(False, description="流式输出"), - local_doc_url: bool = Body(False, description="知识文件返回本地路径(true)或URL(false)"), - request: Request = None, - api_key: str = Body(os.environ.get("OPENAI_API_KEY")), - api_base_url: str = Body(os.environ.get("API_BASE_URL")), - embed_model: str = Body("", ), - embed_model_path: str = Body("", ), - embed_engine: str = Body("", ), - model_name: str = Body("", ), - temperature: float = Body(0.5, ), - model_device: str = Body("", ), - **kargs - ): - params = locals() - params.pop("self", None) - llm_config: LLMConfig = LLMConfig(**params) - embed_config: EmbedConfig = EmbedConfig(**params) - self.engine_name = engine_name if isinstance(engine_name, str) else engine_name.default - self.top_k = top_k if isinstance(top_k, int) else top_k.default - self.score_threshold = score_threshold if isinstance(score_threshold, float) else score_threshold.default - self.stream = stream if isinstance(stream, bool) else stream.default - self.local_doc_url = local_doc_url if isinstance(local_doc_url, bool) else local_doc_url.default - self.request = request - return self._chat(query, history, llm_config, embed_config, **kargs) - - def _chat(self, query: str, history: List[History], llm_config: LLMConfig, embed_config: EmbedConfig, **kargs): - history = [History(**h) if isinstance(h, dict) else h for h in history] - - ## check service dependcy is ok - service_status = self.check_service_status() - - if service_status.code!=200: return service_status - - def chat_iterator(query: str, history: List[History]): - # model = getChatModel() - model = getChatModelFromConfig(llm_config) - - result, content = self.create_task(query, history, model, llm_config, embed_config, **kargs) - logger.info('result={}'.format(result)) - logger.info('content={}'.format(content)) - - if self.stream: - for token in content["text"]: - result["answer"] = token - yield json.dumps(result, ensure_ascii=False) - else: - for token in content["text"]: - result["answer"] += token - yield json.dumps(result, ensure_ascii=False) - - return StreamingResponse(chat_iterator(query, history), - media_type="text/event-stream") - - def achat( - self, - query: str = Body(..., description="用户输入", examples=["hello"]), - history: List[History] = Body( - [], description="历史对话", - examples=[[{"role": "user", "content": "我们来玩成语接龙,我先来,生龙活虎"}]] - ), - engine_name: str = Body(..., description="知识库名称", examples=["samples"]), - top_k: int = Body(5, description="匹配向量数"), - score_threshold: float = Body(1, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1), - stream: bool = Body(False, description="流式输出"), - local_doc_url: bool = Body(False, description="知识文件返回本地路径(true)或URL(false)"), - request: Request = None, - api_key: str = Body(os.environ.get("OPENAI_API_KEY")), - api_base_url: str = Body(os.environ.get("API_BASE_URL")), - embed_model: str = Body("", ), - embed_model_path: str = Body("", ), - embed_engine: str = Body("", ), - model_name: str = Body("", ), - temperature: float = Body(0.5, ), - model_device: str = Body("", ), - ): - # - params = locals() - params.pop("self", None) - llm_config: LLMConfig = LLMConfig(**params) - embed_config: EmbedConfig = EmbedConfig(**params) - self.engine_name = engine_name if isinstance(engine_name, str) else engine_name.default - self.top_k = top_k if isinstance(top_k, int) else top_k.default - self.score_threshold = score_threshold if isinstance(score_threshold, float) else score_threshold.default - self.stream = stream if isinstance(stream, bool) else stream.default - self.local_doc_url = local_doc_url if isinstance(local_doc_url, bool) else local_doc_url.default - self.request = request - return self._achat(query, history, llm_config, embed_config) - - def _achat(self, query: str, history: List[History], llm_config: LLMConfig, embed_config: EmbedConfig): - history = [History(**h) if isinstance(h, dict) else h for h in history] - ## check service dependcy is ok - service_status = self.check_service_status() - if service_status.code!=200: return service_status - - async def chat_iterator(query, history): - callback = AsyncIteratorCallbackHandler() - # model = getChatModel() - model = getChatModelFromConfig(llm_config) - - task, result = self.create_atask(query, history, model, llm_config, embed_config, callback) - if self.stream: - for token in callback["text"]: - result["answer"] = token - yield json.dumps(result, ensure_ascii=False) - else: - for token in callback["text"]: - result["answer"] += token - yield json.dumps(result, ensure_ascii=False) - await task - - return StreamingResponse(chat_iterator(query, history), - media_type="text/event-stream") - - def create_task(self, query: str, history: List[History], model, llm_config: LLMConfig, embed_config: EmbedConfig, **kargs): - '''构建 llm 生成任务''' - chat_prompt = ChatPromptTemplate.from_messages( - [i.to_msg_tuple() for i in history] + [("human", "{input}")] - ) - chain = LLMChain(prompt=chat_prompt, llm=model) - content = chain({"input": query}) - return {"answer": "", "docs": ""}, content - - def create_atask(self, query, history, model, llm_config: LLMConfig, embed_config: EmbedConfig, callback: AsyncIteratorCallbackHandler): - chat_prompt = ChatPromptTemplate.from_messages( - [i.to_msg_tuple() for i in history] + [("human", "{input}")] - ) - chain = LLMChain(prompt=chat_prompt, llm=model) - task = asyncio.create_task(wrap_done( - chain.acall({"input": query}), callback.done - )) - return task, {"answer": "", "docs": ""} \ No newline at end of file diff --git a/coagent/chat/code_chat.py b/coagent/chat/code_chat.py deleted file mode 100644 index 4e19c60..0000000 --- a/coagent/chat/code_chat.py +++ /dev/null @@ -1,174 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: code_chat.py -@time: 2023/10/24 下午4:04 -@desc: -''' - -from fastapi import Request, Body -import os, asyncio -from typing import List -from fastapi.responses import StreamingResponse - -from langchain import LLMChain -from langchain.callbacks import AsyncIteratorCallbackHandler -from langchain.prompts.chat import ChatPromptTemplate - -# from configs.model_config import ( -# llm_model_dict, LLM_MODEL, PROMPT_TEMPLATE, -# VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD, CODE_PROMPT_TEMPLATE) -from coagent.connector.configs.prompts import CODE_PROMPT_TEMPLATE -from coagent.chat.utils import History, wrap_done -from coagent.utils import BaseResponse -from .base_chat import Chat -from coagent.llm_models import getChatModelFromConfig - -from coagent.llm_models.llm_config import LLMConfig, EmbedConfig - - -from coagent.service.cb_api import search_code, cb_exists_api -from loguru import logger -import json - - -class CodeChat(Chat): - - def __init__( - self, - code_base_name: str = '', - code_limit: int = 1, - stream: bool = False, - request: Request = None, - ) -> None: - super().__init__(engine_name=code_base_name, stream=stream) - self.engine_name = code_base_name - self.code_limit = code_limit - self.request = request - self.history_node_list = [] - - def check_service_status(self) -> BaseResponse: - cb = cb_exists_api(self.engine_name) - if not cb: - return BaseResponse(code=404, msg=f"未找到代码库 {self.engine_name}") - return BaseResponse(code=200, msg=f"找到代码库 {self.engine_name}") - - def _process(self, query: str, history: List[History], model, llm_config: LLMConfig, embed_config: EmbedConfig): - '''process''' - - codes_res = search_code(query=query, cb_name=self.engine_name, code_limit=self.code_limit, - search_type=self.cb_search_type, - history_node_list=self.history_node_list, - api_key=llm_config.api_key, - api_base_url=llm_config.api_base_url, - model_name=llm_config.model_name, - temperature=llm_config.temperature, - embed_model=embed_config.embed_model, - embed_model_path=embed_config.embed_model_path, - embed_engine=embed_config.embed_engine, - model_device=embed_config.model_device, - embed_config=embed_config - ) - - context = codes_res['context'] - related_vertices = codes_res['related_vertices'] - - # update node names - # node_names = [node[0] for node in nodes] - # self.history_node_list.extend(node_names) - # self.history_node_list = list(set(self.history_node_list)) - - source_nodes = [] - - for inum, node_name in enumerate(related_vertices[0:5]): - source_nodes.append(f'{inum + 1}. 节点名: `{node_name}`') - - logger.info('history={}'.format(history)) - logger.info('message={}'.format([i.to_msg_tuple() for i in history] + [("human", CODE_PROMPT_TEMPLATE)])) - chat_prompt = ChatPromptTemplate.from_messages( - [i.to_msg_tuple() for i in history] + [("human", CODE_PROMPT_TEMPLATE)] - ) - logger.info('chat_prompt={}'.format(chat_prompt)) - chain = LLMChain(prompt=chat_prompt, llm=model) - result = {"answer": "", "codes": source_nodes} - return chain, context, result - - def chat( - self, - query: str = Body(..., description="用户输入", examples=["hello"]), - history: List[History] = Body( - [], description="历史对话", - examples=[[{"role": "user", "content": "我们来玩成语接龙,我先来,生龙活虎"}]] - ), - engine_name: str = Body(..., description="知识库名称", examples=["samples"]), - code_limit: int = Body(1, examples=['1']), - cb_search_type: str = Body('', examples=['1']), - stream: bool = Body(False, description="流式输出"), - local_doc_url: bool = Body(False, description="知识文件返回本地路径(true)或URL(false)"), - request: Request = None, - - api_key: str = Body(os.environ.get("OPENAI_API_KEY")), - api_base_url: str = Body(os.environ.get("API_BASE_URL")), - embed_model: str = Body("", ), - embed_model_path: str = Body("", ), - embed_engine: str = Body("", ), - model_name: str = Body("", ), - temperature: float = Body(0.5, ), - model_device: str = Body("", ), - **kargs - ): - params = locals() - params.pop("self") - llm_config: LLMConfig = LLMConfig(**params) - embed_config: EmbedConfig = EmbedConfig(**params) - self.engine_name = engine_name if isinstance(engine_name, str) else engine_name.default - self.code_limit = code_limit - self.stream = stream if isinstance(stream, bool) else stream.default - self.local_doc_url = local_doc_url if isinstance(local_doc_url, bool) else local_doc_url.default - self.request = request - self.cb_search_type = cb_search_type - return self._chat(query, history, llm_config, embed_config, **kargs) - - def _chat(self, query: str, history: List[History], llm_config: LLMConfig, embed_config: EmbedConfig, **kargs): - history = [History(**h) if isinstance(h, dict) else h for h in history] - - service_status = self.check_service_status() - - if service_status.code != 200: return service_status - - def chat_iterator(query: str, history: List[History]): - # model = getChatModel() - model = getChatModelFromConfig(llm_config) - - result, content = self.create_task(query, history, model, llm_config, embed_config, **kargs) - # logger.info('result={}'.format(result)) - # logger.info('content={}'.format(content)) - - if self.stream: - for token in content["text"]: - result["answer"] = token - yield json.dumps(result, ensure_ascii=False) - else: - for token in content["text"]: - result["answer"] += token - yield json.dumps(result, ensure_ascii=False) - - return StreamingResponse(chat_iterator(query, history), - media_type="text/event-stream") - - def create_task(self, query: str, history: List[History], model, llm_config: LLMConfig, embed_config: EmbedConfig): - '''构建 llm 生成任务''' - chain, context, result = self._process(query, history, model, llm_config, embed_config) - logger.info('chain={}'.format(chain)) - try: - content = chain({"context": context, "question": query}) - except Exception as e: - content = {"text": str(e)} - return result, content - - def create_atask(self, query, history, model, llm_config: LLMConfig, embed_config: EmbedConfig, callback: AsyncIteratorCallbackHandler): - chain, context, result = self._process(query, history, model, llm_config, embed_config) - task = asyncio.create_task(wrap_done( - chain.acall({"context": context, "question": query}), callback.done - )) - return task, result diff --git a/coagent/chat/knowledge_chat.py b/coagent/chat/knowledge_chat.py deleted file mode 100644 index 2e2bdcd..0000000 --- a/coagent/chat/knowledge_chat.py +++ /dev/null @@ -1,89 +0,0 @@ -from fastapi import Request -import os, asyncio -from urllib.parse import urlencode -from typing import List - -from langchain import LLMChain -from langchain.callbacks import AsyncIteratorCallbackHandler -from langchain.prompts.chat import ChatPromptTemplate - - -# from configs.model_config import ( -# llm_model_dict, LLM_MODEL, PROMPT_TEMPLATE, -# VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD) -from coagent.base_configs.env_config import KB_ROOT_PATH -from coagent.connector.configs.prompts import ORIGIN_TEMPLATE_PROMPT -from coagent.chat.utils import History, wrap_done -from coagent.utils import BaseResponse -from coagent.llm_models.llm_config import LLMConfig, EmbedConfig -from .base_chat import Chat -from coagent.service.kb_api import search_docs, KBServiceFactory -from loguru import logger - - -class KnowledgeChat(Chat): - - def __init__( - self, - engine_name: str = "", - top_k: int = 5, - stream: bool = False, - score_thresold: float = 1.0, - local_doc_url: bool = False, - request: Request = None, - kb_root_path: str = KB_ROOT_PATH, - ) -> None: - super().__init__(engine_name, top_k, stream) - self.score_thresold = score_thresold - self.local_doc_url = local_doc_url - self.request = request - self.kb_root_path = kb_root_path - - def check_service_status(self) -> BaseResponse: - kb = KBServiceFactory.get_service_by_name(self.engine_name, self.kb_root_path) - if kb is None: - return BaseResponse(code=404, msg=f"未找到知识库 {self.engine_name}") - return BaseResponse(code=200, msg=f"找到知识库 {self.engine_name}") - - def _process(self, query: str, history: List[History], model, llm_config: LLMConfig, embed_config: EmbedConfig, ): - '''process''' - docs = search_docs( - query, self.engine_name, self.top_k, self.score_threshold, self.kb_root_path, - api_key=embed_config.api_key, api_base_url=embed_config.api_base_url, embed_model=embed_config.embed_model, - embed_model_path=embed_config.embed_model_path, embed_engine=embed_config.embed_engine, - model_device=embed_config.model_device, - ) - context = "\n".join([doc.page_content for doc in docs]) - source_documents = [] - for inum, doc in enumerate(docs): - filename = os.path.split(doc.metadata["source"])[-1] - if self.local_doc_url: - url = "file://" + doc.metadata["source"] - else: - parameters = urlencode({"knowledge_base_name": self.engine_name, "file_name":filename}) - url = f"{self.request.base_url}knowledge_base/download_doc?" + parameters - text = f"""出处 [{inum + 1}] [{filename}]({url}) \n\n{doc.page_content}\n\n""" - source_documents.append(text) - chat_prompt = ChatPromptTemplate.from_messages( - [i.to_msg_tuple() for i in history] + [("human", ORIGIN_TEMPLATE_PROMPT)] - ) - chain = LLMChain(prompt=chat_prompt, llm=model) - result = {"answer": "", "docs": source_documents} - return chain, context, result - - def create_task(self, query: str, history: List[History], model, llm_config: LLMConfig, embed_config: EmbedConfig, ): - '''构建 llm 生成任务''' - logger.debug(f"query: {query}, history: {history}") - chain, context, result = self._process(query, history, model, llm_config, embed_config) - try: - content = chain({"context": context, "question": query}) - except Exception as e: - content = {"text": str(e)} - return result, content - - def create_atask(self, query, history, model, llm_config: LLMConfig, embed_config: EmbedConfig, callback: AsyncIteratorCallbackHandler): - chain, context, result = self._process(query, history, model, llm_config, embed_config) - task = asyncio.create_task(wrap_done( - chain.acall({"context": context, "question": query}), callback.done - )) - return task, result diff --git a/coagent/chat/llm_chat.py b/coagent/chat/llm_chat.py deleted file mode 100644 index 2c36f96..0000000 --- a/coagent/chat/llm_chat.py +++ /dev/null @@ -1,42 +0,0 @@ -import asyncio -from typing import List - -from langchain import LLMChain -from langchain.callbacks import AsyncIteratorCallbackHandler -from langchain.prompts.chat import ChatPromptTemplate - - -from coagent.chat.utils import History, wrap_done -from coagent.llm_models.llm_config import LLMConfig, EmbedConfig -from .base_chat import Chat -from loguru import logger - - -class LLMChat(Chat): - - def __init__( - self, - engine_name: str = "", - top_k: int = 1, - stream: bool = False, - ) -> None: - super().__init__(engine_name, top_k, stream) - - def create_task(self, query: str, history: List[History], model, llm_config: LLMConfig, embed_config: EmbedConfig, **kargs): - '''构建 llm 生成任务''' - chat_prompt = ChatPromptTemplate.from_messages( - [i.to_msg_tuple() for i in history] + [("human", "{input}")] - ) - chain = LLMChain(prompt=chat_prompt, llm=model) - content = chain({"input": query}) - return {"answer": "", "docs": ""}, content - - def create_atask(self, query, history, model, llm_config: LLMConfig, embed_config: EmbedConfig, callback: AsyncIteratorCallbackHandler): - chat_prompt = ChatPromptTemplate.from_messages( - [i.to_msg_tuple() for i in history] + [("human", "{input}")] - ) - chain = LLMChain(prompt=chat_prompt, llm=model) - task = asyncio.create_task(wrap_done( - chain.acall({"input": query}), callback.done - )) - return task, {"answer": "", "docs": ""} \ No newline at end of file diff --git a/coagent/chat/search_chat.py b/coagent/chat/search_chat.py deleted file mode 100644 index bf929e9..0000000 --- a/coagent/chat/search_chat.py +++ /dev/null @@ -1,151 +0,0 @@ -import os, asyncio -from typing import List, Optional, Dict - -from langchain import LLMChain -from langchain.callbacks import AsyncIteratorCallbackHandler -from langchain.utilities import BingSearchAPIWrapper, DuckDuckGoSearchAPIWrapper -from langchain.prompts.chat import ChatPromptTemplate -from langchain.docstore.document import Document - -# from configs.model_config import ( -# PROMPT_TEMPLATE, SEARCH_ENGINE_TOP_K, BING_SUBSCRIPTION_KEY, BING_SEARCH_URL, -# VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD) -from coagent.connector.configs.prompts import ORIGIN_TEMPLATE_PROMPT -from coagent.chat.utils import History, wrap_done -from coagent.utils import BaseResponse -from coagent.llm_models.llm_config import LLMConfig, EmbedConfig -from .base_chat import Chat - -from loguru import logger - -from duckduckgo_search import DDGS - - -# def bing_search(text, result_len=5): -# if not (BING_SEARCH_URL and BING_SUBSCRIPTION_KEY): -# return [{"snippet": "please set BING_SUBSCRIPTION_KEY and BING_SEARCH_URL in os ENV", -# "title": "env info is not found", -# "link": "https://python.langchain.com/en/latest/modules/agents/tools/examples/bing_search.html"}] -# search = BingSearchAPIWrapper(bing_subscription_key=BING_SUBSCRIPTION_KEY, -# bing_search_url=BING_SEARCH_URL) -# return search.results(text, result_len) - - -def duckduckgo_search( - query: str, - result_len: int = 5, - region: Optional[str] = "wt-wt", - safesearch: str = "moderate", - time: Optional[str] = "y", - backend: str = "api", - ): - with DDGS(proxies=os.environ.get("DUCKDUCKGO_PROXY")) as ddgs: - results = ddgs.text( - query, - region=region, - safesearch=safesearch, - timelimit=time, - backend=backend, - ) - if results is None: - return [{"Result": "No good DuckDuckGo Search Result was found"}] - - def to_metadata(result: Dict) -> Dict[str, str]: - if backend == "news": - return { - "date": result["date"], - "title": result["title"], - "snippet": result["body"], - "source": result["source"], - "link": result["url"], - } - return { - "snippet": result["body"], - "title": result["title"], - "link": result["href"], - } - - formatted_results = [] - for i, res in enumerate(results, 1): - if res is not None: - formatted_results.append(to_metadata(res)) - if len(formatted_results) == result_len: - break - return formatted_results - - -# def duckduckgo_search(text, result_len=SEARCH_ENGINE_TOP_K): -# search = DuckDuckGoSearchAPIWrapper() -# return search.results(text, result_len) - - -SEARCH_ENGINES = {"duckduckgo": duckduckgo_search, - # "bing": bing_search, - } - - -def search_result2docs(search_results): - docs = [] - for result in search_results: - doc = Document(page_content=result["snippet"] if "snippet" in result.keys() else "", - metadata={"source": result["link"] if "link" in result.keys() else "", - "filename": result["title"] if "title" in result.keys() else ""}) - docs.append(doc) - return docs - - -def lookup_search_engine( - query: str, - search_engine_name: str, - top_k: int = 5, -): - results = SEARCH_ENGINES[search_engine_name](query, result_len=top_k) - docs = search_result2docs(results) - return docs - - - -class SearchChat(Chat): - - def __init__( - self, - engine_name: str = "", - top_k: int = 5, - stream: bool = False, - ) -> None: - super().__init__(engine_name, top_k, stream) - - def check_service_status(self) -> BaseResponse: - if self.engine_name not in SEARCH_ENGINES.keys(): - return BaseResponse(code=404, msg=f"未支持搜索引擎 {self.engine_name}") - return BaseResponse(code=200, msg=f"支持搜索引擎 {self.engine_name}") - - def _process(self, query: str, history: List[History], model): - '''process''' - docs = lookup_search_engine(query, self.engine_name, self.top_k) - context = "\n".join([doc.page_content for doc in docs]) - - source_documents = [ - f"""出处 [{inum + 1}] [{doc.metadata["source"]}]({doc.metadata["source"]}) \n\n{doc.page_content}\n\n""" - for inum, doc in enumerate(docs) - ] - - chat_prompt = ChatPromptTemplate.from_messages( - [i.to_msg_tuple() for i in history] + [("human", ORIGIN_TEMPLATE_PROMPT)] - ) - chain = LLMChain(prompt=chat_prompt, llm=model) - result = {"answer": "", "docs": source_documents} - return chain, context, result - - def create_task(self, query: str, history: List[History], model, llm_config: LLMConfig, embed_config: EmbedConfig, ): - '''构建 llm 生成任务''' - chain, context, result = self._process(query, history, model) - content = chain({"context": context, "question": query}) - return result, content - - def create_atask(self, query, history, model, llm_config: LLMConfig, embed_config: EmbedConfig, callback: AsyncIteratorCallbackHandler): - chain, context, result = self._process(query, history, model) - task = asyncio.create_task(wrap_done( - chain.acall({"context": context, "question": query}), callback.done - )) - return task, result diff --git a/coagent/chat/utils.py b/coagent/chat/utils.py deleted file mode 100644 index f8afb10..0000000 --- a/coagent/chat/utils.py +++ /dev/null @@ -1,30 +0,0 @@ -import asyncio -from typing import Awaitable -from pydantic import BaseModel, Field - - -async def wrap_done(fn: Awaitable, event: asyncio.Event): - """Wrap an awaitable with a event to signal when it's done or an exception is raised.""" - try: - await fn - except Exception as e: - # TODO: handle exception - print(f"Caught exception: {e}") - finally: - # Signal the aiter to stop. - event.set() - - -class History(BaseModel): - """ - 对话历史 - 可从dict生成,如 - h = History(**{"role":"user","content":"你好"}) - 也可转换为tuple,如 - h.to_msy_tuple = ("human", "你好") - """ - role: str = Field(...) - content: str = Field(...) - - def to_msg_tuple(self): - return "ai" if self.role=="assistant" else "human", self.content diff --git a/coagent/codechat/__init__.py b/coagent/codechat/__init__.py deleted file mode 100644 index 35197bf..0000000 --- a/coagent/codechat/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: __init__.py.py -@time: 2023/11/21 下午2:01 -@desc: -''' \ No newline at end of file diff --git a/coagent/codechat/code_analyzer/__init__.py b/coagent/codechat/code_analyzer/__init__.py deleted file mode 100644 index 7704e55..0000000 --- a/coagent/codechat/code_analyzer/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: __init__.py.py -@time: 2023/11/21 下午2:27 -@desc: -''' \ No newline at end of file diff --git a/coagent/codechat/code_analyzer/code_analyzer.py b/coagent/codechat/code_analyzer/code_analyzer.py deleted file mode 100644 index f725ea1..0000000 --- a/coagent/codechat/code_analyzer/code_analyzer.py +++ /dev/null @@ -1,222 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: code_analyzer.py -@time: 2023/11/21 下午2:27 -@desc: -''' -import time -from loguru import logger - -from coagent.codechat.code_analyzer.code_static_analysis import CodeStaticAnalysis -from coagent.codechat.code_analyzer.code_intepreter import CodeIntepreter -from coagent.codechat.code_analyzer.code_preprocess import CodePreprocessor -from coagent.codechat.code_analyzer.code_dedup import CodeDedup -from coagent.llm_models.llm_config import LLMConfig - - - -class CodeAnalyzer: - def __init__(self, language: str, llm_config: LLMConfig): - self.llm_config = llm_config - self.code_preprocessor = CodePreprocessor() - self.code_debup = CodeDedup() - self.code_interperter = CodeIntepreter(self.llm_config) - self.code_static_analyzer = CodeStaticAnalysis(language=language) - - def analyze(self, code_dict: dict, do_interpret: bool = True): - ''' - analyze code - @param code_dict: {fp: code_text} - @param do_interpret: Whether to get analysis result - @return: - ''' - # preprocess and dedup - st = time.time() - code_dict = self.code_preprocessor.preprocess(code_dict) - code_dict = self.code_debup.dedup(code_dict) - logger.debug('preprocess and dedup rt={}'.format(time.time() - st)) - - # static analysis - st = time.time() - static_analysis_res = self.code_static_analyzer.analyze(code_dict) - logger.debug('static analysis rt={}'.format(time.time() - st)) - - # interpretation - if do_interpret: - logger.info('start interpret code') - st = time.time() - code_list = list(code_dict.values()) - interpretation = self.code_interperter.get_intepretation_batch(code_list) - logger.debug('interpret rt={}'.format(time.time() - st)) - else: - interpretation = {i: '' for i in code_dict.values()} - - return static_analysis_res, interpretation - - -if __name__ == '__main__': - engine = 'openai' - language = 'java' - code_dict = {'1': '''package com.theokanning.openai.client; -import com.theokanning.openai.DeleteResult; -import com.theokanning.openai.OpenAiResponse; -import com.theokanning.openai.audio.TranscriptionResult; -import com.theokanning.openai.audio.TranslationResult; -import com.theokanning.openai.billing.BillingUsage; -import com.theokanning.openai.billing.Subscription; -import com.theokanning.openai.completion.CompletionRequest; -import com.theokanning.openai.completion.CompletionResult; -import com.theokanning.openai.completion.chat.ChatCompletionRequest; -import com.theokanning.openai.completion.chat.ChatCompletionResult; -import com.theokanning.openai.edit.EditRequest; -import com.theokanning.openai.edit.EditResult; -import com.theokanning.openai.embedding.EmbeddingRequest; -import com.theokanning.openai.embedding.EmbeddingResult; -import com.theokanning.openai.engine.Engine; -import com.theokanning.openai.file.File; -import com.theokanning.openai.fine_tuning.FineTuningEvent; -import com.theokanning.openai.fine_tuning.FineTuningJob; -import com.theokanning.openai.fine_tuning.FineTuningJobRequest; -import com.theokanning.openai.finetune.FineTuneEvent; -import com.theokanning.openai.finetune.FineTuneRequest; -import com.theokanning.openai.finetune.FineTuneResult; -import com.theokanning.openai.image.CreateImageRequest; -import com.theokanning.openai.image.ImageResult; -import com.theokanning.openai.model.Model; -import com.theokanning.openai.moderation.ModerationRequest; -import com.theokanning.openai.moderation.ModerationResult; -import io.reactivex.Single; -import okhttp3.MultipartBody; -import okhttp3.RequestBody; -import okhttp3.ResponseBody; -import retrofit2.Call; -import retrofit2.http.*; -import java.time.LocalDate; -public interface OpenAiApi { - @GET("v1/models") - Single> listModels(); - @GET("/v1/models/{model_id}") - Single getModel(@Path("model_id") String modelId); - @POST("/v1/completions") - Single createCompletion(@Body CompletionRequest request); - @Streaming - @POST("/v1/completions") - Call createCompletionStream(@Body CompletionRequest request); - @POST("/v1/chat/completions") - Single createChatCompletion(@Body ChatCompletionRequest request); - @Streaming - @POST("/v1/chat/completions") - Call createChatCompletionStream(@Body ChatCompletionRequest request); - @Deprecated - @POST("/v1/engines/{engine_id}/completions") - Single createCompletion(@Path("engine_id") String engineId, @Body CompletionRequest request); - @POST("/v1/edits") - Single createEdit(@Body EditRequest request); - @Deprecated - @POST("/v1/engines/{engine_id}/edits") - Single createEdit(@Path("engine_id") String engineId, @Body EditRequest request); - @POST("/v1/embeddings") - Single createEmbeddings(@Body EmbeddingRequest request); - @Deprecated - @POST("/v1/engines/{engine_id}/embeddings") - Single createEmbeddings(@Path("engine_id") String engineId, @Body EmbeddingRequest request); - @GET("/v1/files") - Single> listFiles(); - @Multipart - @POST("/v1/files") - Single uploadFile(@Part("purpose") RequestBody purpose, @Part MultipartBody.Part file); - @DELETE("/v1/files/{file_id}") - Single deleteFile(@Path("file_id") String fileId); - @GET("/v1/files/{file_id}") - Single retrieveFile(@Path("file_id") String fileId); - @Streaming - @GET("/v1/files/{file_id}/content") - Single retrieveFileContent(@Path("file_id") String fileId); - @POST("/v1/fine_tuning/jobs") - Single createFineTuningJob(@Body FineTuningJobRequest request); - @GET("/v1/fine_tuning/jobs") - Single> listFineTuningJobs(); - @GET("/v1/fine_tuning/jobs/{fine_tuning_job_id}") - Single retrieveFineTuningJob(@Path("fine_tuning_job_id") String fineTuningJobId); - @POST("/v1/fine_tuning/jobs/{fine_tuning_job_id}/cancel") - Single cancelFineTuningJob(@Path("fine_tuning_job_id") String fineTuningJobId); - @GET("/v1/fine_tuning/jobs/{fine_tuning_job_id}/events") - Single> listFineTuningJobEvents(@Path("fine_tuning_job_id") String fineTuningJobId); - @Deprecated - @POST("/v1/fine-tunes") - Single createFineTune(@Body FineTuneRequest request); - @POST("/v1/completions") - Single createFineTuneCompletion(@Body CompletionRequest request); - @Deprecated - @GET("/v1/fine-tunes") - Single> listFineTunes(); - @Deprecated - @GET("/v1/fine-tunes/{fine_tune_id}") - Single retrieveFineTune(@Path("fine_tune_id") String fineTuneId); - @Deprecated - @POST("/v1/fine-tunes/{fine_tune_id}/cancel") - Single cancelFineTune(@Path("fine_tune_id") String fineTuneId); - @Deprecated - @GET("/v1/fine-tunes/{fine_tune_id}/events") - Single> listFineTuneEvents(@Path("fine_tune_id") String fineTuneId); - @DELETE("/v1/models/{fine_tune_id}") - Single deleteFineTune(@Path("fine_tune_id") String fineTuneId); - @POST("/v1/images/generations") - Single createImage(@Body CreateImageRequest request); - @POST("/v1/images/edits") - Single createImageEdit(@Body RequestBody requestBody); - @POST("/v1/images/variations") - Single createImageVariation(@Body RequestBody requestBody); - @POST("/v1/audio/transcriptions") - Single createTranscription(@Body RequestBody requestBody); - @POST("/v1/audio/translations") - Single createTranslation(@Body RequestBody requestBody); - @POST("/v1/moderations") - Single createModeration(@Body ModerationRequest request); - @Deprecated - @GET("v1/engines") - Single> getEngines(); - @Deprecated - @GET("/v1/engines/{engine_id}") - Single getEngine(@Path("engine_id") String engineId); - /** - * Account information inquiry: It contains total amount (in US dollars) and other information. - * - * @return - */ - @Deprecated - @GET("v1/dashboard/billing/subscription") - Single subscription(); - /** - * Account call interface consumption amount inquiry. - * totalUsage = Total amount used by the account (in US cents). - * - * @param starDate - * @param endDate - * @return Consumption amount information. - */ - @Deprecated - @GET("v1/dashboard/billing/usage") - Single billingUsage(@Query("start_date") LocalDate starDate, @Query("end_date") LocalDate endDate); -}''', '2': ''' -package com.theokanning.openai; - -/** - * OkHttp Interceptor that adds an authorization token header - * - * @deprecated Use {@link com.theokanning.openai.client.AuthenticationInterceptor} - */ -@Deprecated -public class AuthenticationInterceptor extends com.theokanning.openai.client.AuthenticationInterceptor { - - AuthenticationInterceptor(String token) { - super(token); - } - -} -'''} - - ca = CodeAnalyzer(engine, language) - res = ca.analyze(code_dict) - logger.debug(res) diff --git a/coagent/codechat/code_analyzer/code_dedup.py b/coagent/codechat/code_analyzer/code_dedup.py deleted file mode 100644 index 071bd46..0000000 --- a/coagent/codechat/code_analyzer/code_dedup.py +++ /dev/null @@ -1,31 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: code_dedup.py -@time: 2023/11/21 下午2:27 -@desc: -''' -# encoding: utf-8 -''' -@author: 温进 -@file: java_dedup.py -@time: 2023/10/23 下午5:02 -@desc: -''' - - -class CodeDedup: - def __init__(self): - pass - - def dedup(self, code_dict): - code_dict = self.exact_dedup(code_dict) - return code_dict - - def exact_dedup(self, code_dict): - res = {} - for fp, code_text in code_dict.items(): - if code_text not in res.values(): - res[fp] = code_text - - return res \ No newline at end of file diff --git a/coagent/codechat/code_analyzer/code_intepreter.py b/coagent/codechat/code_analyzer/code_intepreter.py deleted file mode 100644 index 7e321b6..0000000 --- a/coagent/codechat/code_analyzer/code_intepreter.py +++ /dev/null @@ -1,238 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: code_intepreter.py -@time: 2023/11/22 上午11:57 -@desc: -''' -from loguru import logger -from langchain.schema import ( - HumanMessage, -) - -# from configs.model_config import CODE_INTERPERT_TEMPLATE -from coagent.connector.configs.prompts import CODE_INTERPERT_TEMPLATE -from coagent.llm_models.openai_model import getChatModelFromConfig -from coagent.llm_models.llm_config import LLMConfig - - -class CodeIntepreter: - def __init__(self, llm_config: LLMConfig): - self.llm_config = llm_config - - def get_intepretation(self, code_list): - ''' - get intepretion of code - @param code_list: - @return: - ''' - # chat_model = getChatModel() - chat_model = getChatModelFromConfig(self.llm_config) - - res = {} - for code in code_list: - message = CODE_INTERPERT_TEMPLATE.format(code=code) - message = [HumanMessage(content=message)] - chat_res = chat_model.predict_messages(message) - content = chat_res.content - res[code] = content - return res - - def get_intepretation_batch(self, code_list): - ''' - get intepretion of code - @param code_list: - @return: - ''' - # chat_model = getChatModel() - chat_model = getChatModelFromConfig(self.llm_config) - - res = {} - messages = [] - for code in code_list: - message = CODE_INTERPERT_TEMPLATE.format(code=code) - messages.append(message) - - try: - chat_ress = [chat_model(messages) for message in messages] - except: - chat_ress = chat_model.batch(messages) - for chat_res, code in zip(chat_ress, code_list): - try: - res[code] = chat_res.content - except: - res[code] = chat_res - return res - - - - -if __name__ == '__main__': - engine = 'openai' - code_list = ['''package com.theokanning.openai.client; -import com.theokanning.openai.DeleteResult; -import com.theokanning.openai.OpenAiResponse; -import com.theokanning.openai.audio.TranscriptionResult; -import com.theokanning.openai.audio.TranslationResult; -import com.theokanning.openai.billing.BillingUsage; -import com.theokanning.openai.billing.Subscription; -import com.theokanning.openai.completion.CompletionRequest; -import com.theokanning.openai.completion.CompletionResult; -import com.theokanning.openai.completion.chat.ChatCompletionRequest; -import com.theokanning.openai.completion.chat.ChatCompletionResult; -import com.theokanning.openai.edit.EditRequest; -import com.theokanning.openai.edit.EditResult; -import com.theokanning.openai.embedding.EmbeddingRequest; -import com.theokanning.openai.embedding.EmbeddingResult; -import com.theokanning.openai.engine.Engine; -import com.theokanning.openai.file.File; -import com.theokanning.openai.fine_tuning.FineTuningEvent; -import com.theokanning.openai.fine_tuning.FineTuningJob; -import com.theokanning.openai.fine_tuning.FineTuningJobRequest; -import com.theokanning.openai.finetune.FineTuneEvent; -import com.theokanning.openai.finetune.FineTuneRequest; -import com.theokanning.openai.finetune.FineTuneResult; -import com.theokanning.openai.image.CreateImageRequest; -import com.theokanning.openai.image.ImageResult; -import com.theokanning.openai.model.Model; -import com.theokanning.openai.moderation.ModerationRequest; -import com.theokanning.openai.moderation.ModerationResult; -import io.reactivex.Single; -import okhttp3.MultipartBody; -import okhttp3.RequestBody; -import okhttp3.ResponseBody; -import retrofit2.Call; -import retrofit2.http.*; -import java.time.LocalDate; -public interface OpenAiApi { - @GET("v1/models") - Single> listModels(); - @GET("/v1/models/{model_id}") - Single getModel(@Path("model_id") String modelId); - @POST("/v1/completions") - Single createCompletion(@Body CompletionRequest request); - @Streaming - @POST("/v1/completions") - Call createCompletionStream(@Body CompletionRequest request); - @POST("/v1/chat/completions") - Single createChatCompletion(@Body ChatCompletionRequest request); - @Streaming - @POST("/v1/chat/completions") - Call createChatCompletionStream(@Body ChatCompletionRequest request); - @Deprecated - @POST("/v1/engines/{engine_id}/completions") - Single createCompletion(@Path("engine_id") String engineId, @Body CompletionRequest request); - @POST("/v1/edits") - Single createEdit(@Body EditRequest request); - @Deprecated - @POST("/v1/engines/{engine_id}/edits") - Single createEdit(@Path("engine_id") String engineId, @Body EditRequest request); - @POST("/v1/embeddings") - Single createEmbeddings(@Body EmbeddingRequest request); - @Deprecated - @POST("/v1/engines/{engine_id}/embeddings") - Single createEmbeddings(@Path("engine_id") String engineId, @Body EmbeddingRequest request); - @GET("/v1/files") - Single> listFiles(); - @Multipart - @POST("/v1/files") - Single uploadFile(@Part("purpose") RequestBody purpose, @Part MultipartBody.Part file); - @DELETE("/v1/files/{file_id}") - Single deleteFile(@Path("file_id") String fileId); - @GET("/v1/files/{file_id}") - Single retrieveFile(@Path("file_id") String fileId); - @Streaming - @GET("/v1/files/{file_id}/content") - Single retrieveFileContent(@Path("file_id") String fileId); - @POST("/v1/fine_tuning/jobs") - Single createFineTuningJob(@Body FineTuningJobRequest request); - @GET("/v1/fine_tuning/jobs") - Single> listFineTuningJobs(); - @GET("/v1/fine_tuning/jobs/{fine_tuning_job_id}") - Single retrieveFineTuningJob(@Path("fine_tuning_job_id") String fineTuningJobId); - @POST("/v1/fine_tuning/jobs/{fine_tuning_job_id}/cancel") - Single cancelFineTuningJob(@Path("fine_tuning_job_id") String fineTuningJobId); - @GET("/v1/fine_tuning/jobs/{fine_tuning_job_id}/events") - Single> listFineTuningJobEvents(@Path("fine_tuning_job_id") String fineTuningJobId); - @Deprecated - @POST("/v1/fine-tunes") - Single createFineTune(@Body FineTuneRequest request); - @POST("/v1/completions") - Single createFineTuneCompletion(@Body CompletionRequest request); - @Deprecated - @GET("/v1/fine-tunes") - Single> listFineTunes(); - @Deprecated - @GET("/v1/fine-tunes/{fine_tune_id}") - Single retrieveFineTune(@Path("fine_tune_id") String fineTuneId); - @Deprecated - @POST("/v1/fine-tunes/{fine_tune_id}/cancel") - Single cancelFineTune(@Path("fine_tune_id") String fineTuneId); - @Deprecated - @GET("/v1/fine-tunes/{fine_tune_id}/events") - Single> listFineTuneEvents(@Path("fine_tune_id") String fineTuneId); - @DELETE("/v1/models/{fine_tune_id}") - Single deleteFineTune(@Path("fine_tune_id") String fineTuneId); - @POST("/v1/images/generations") - Single createImage(@Body CreateImageRequest request); - @POST("/v1/images/edits") - Single createImageEdit(@Body RequestBody requestBody); - @POST("/v1/images/variations") - Single createImageVariation(@Body RequestBody requestBody); - @POST("/v1/audio/transcriptions") - Single createTranscription(@Body RequestBody requestBody); - @POST("/v1/audio/translations") - Single createTranslation(@Body RequestBody requestBody); - @POST("/v1/moderations") - Single createModeration(@Body ModerationRequest request); - @Deprecated - @GET("v1/engines") - Single> getEngines(); - @Deprecated - @GET("/v1/engines/{engine_id}") - Single getEngine(@Path("engine_id") String engineId); - /** - * Account information inquiry: It contains total amount (in US dollars) and other information. - * - * @return - */ - @Deprecated - @GET("v1/dashboard/billing/subscription") - Single subscription(); - /** - * Account call interface consumption amount inquiry. - * totalUsage = Total amount used by the account (in US cents). - * - * @param starDate - * @param endDate - * @return Consumption amount information. - */ - @Deprecated - @GET("v1/dashboard/billing/usage") - Single billingUsage(@Query("start_date") LocalDate starDate, @Query("end_date") LocalDate endDate); -}''', ''' -package com.theokanning.openai; - -/** - * OkHttp Interceptor that adds an authorization token header - * - * @deprecated Use {@link com.theokanning.openai.client.AuthenticationInterceptor} - */ -@Deprecated -public class AuthenticationInterceptor extends com.theokanning.openai.client.AuthenticationInterceptor { - - AuthenticationInterceptor(String token) { - super(token); - } - -} -'''] - - ci = CodeIntepreter(engine) - res = ci.get_intepretation_batch(code_list) - logger.debug(res) - - - - - diff --git a/coagent/codechat/code_analyzer/code_preprocess.py b/coagent/codechat/code_analyzer/code_preprocess.py deleted file mode 100644 index 50324ba..0000000 --- a/coagent/codechat/code_analyzer/code_preprocess.py +++ /dev/null @@ -1,14 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: code_preprocess.py -@time: 2023/11/21 下午2:28 -@desc: -''' - -class CodePreprocessor: - def __init__(self): - pass - - def preprocess(self, code_dict): - return code_dict \ No newline at end of file diff --git a/coagent/codechat/code_analyzer/code_static_analysis.py b/coagent/codechat/code_analyzer/code_static_analysis.py deleted file mode 100644 index 4acf6ce..0000000 --- a/coagent/codechat/code_analyzer/code_static_analysis.py +++ /dev/null @@ -1,26 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: code_static_analysis.py -@time: 2023/11/21 下午2:28 -@desc: -''' -from coagent.codechat.code_analyzer.language_static_analysis import * - -class CodeStaticAnalysis: - def __init__(self, language): - self.language = language - - def analyze(self, code_dict): - ''' - analyze code - @param code_list: - @return: - ''' - if self.language == 'java': - analyzer = JavaStaticAnalysis() - else: - raise ValueError('language should be one of [java]') - - analyze_res = analyzer.analyze(code_dict) - return analyze_res diff --git a/coagent/codechat/code_analyzer/language_static_analysis/__init__.py b/coagent/codechat/code_analyzer/language_static_analysis/__init__.py deleted file mode 100644 index c99e049..0000000 --- a/coagent/codechat/code_analyzer/language_static_analysis/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: __init__.py.py -@time: 2023/11/21 下午4:24 -@desc: -''' - -from .java_static_analysis import JavaStaticAnalysis - - -__all__ = [ - 'JavaStaticAnalysis' - ] \ No newline at end of file diff --git a/coagent/codechat/code_analyzer/language_static_analysis/java_static_analysis.py b/coagent/codechat/code_analyzer/language_static_analysis/java_static_analysis.py deleted file mode 100644 index 83bca4e..0000000 --- a/coagent/codechat/code_analyzer/language_static_analysis/java_static_analysis.py +++ /dev/null @@ -1,138 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: java_static_analysis.py -@time: 2023/11/21 下午4:25 -@desc: -''' -import os -from loguru import logger -import javalang - - -class JavaStaticAnalysis: - def __init__(self): - pass - - def analyze(self, java_code_dict): - ''' - parse java code and extract entity - ''' - tree_dict = self.preparse(java_code_dict) - res = self.multi_java_code_parse(tree_dict) - - return res - - def preparse(self, java_code_dict): - ''' - preparse by javalang - < dict of java_code and tree - ''' - tree_dict = {} - for fp, java_code in java_code_dict.items(): - try: - tree = javalang.parse.parse(java_code) - except Exception as e: - continue - - if tree.package is not None: - tree_dict[fp] = {'code': java_code, 'tree': tree} - logger.info('success parse {} files'.format(len(tree_dict))) - return tree_dict - - def single_java_code_parse(self, tree, fp): - ''' - parse single code file - > tree: javalang parse result - < {pac_name: '', class_name_list: [], func_name_dict: {}, import_pac_name_list: []]} - ''' - import_pac_name_list = [] - - # get imports - import_list = tree.imports - - for import_pac in import_list: - import_pac_name = import_pac.path - import_pac_name_list.append(import_pac_name) - - fp_last = fp.split(os.path.sep)[-1] - pac_name = tree.package.name + '#' + fp_last - class_name_list = [] - func_name_dict = {} - - for node in tree.types: - if type(node) in (javalang.tree.ClassDeclaration, javalang.tree.InterfaceDeclaration): - class_name = tree.package.name + '.' + node.name - class_name_list.append(class_name) - - for node_inner in node.body: - if type(node_inner) is javalang.tree.MethodDeclaration: - func_name = class_name + '#' + node_inner.name - - # add params name to func_name - params_list = node_inner.parameters - - for params in params_list: - params_name = params.type.name - func_name = func_name + '-' + params_name - - if class_name not in func_name_dict: - func_name_dict[class_name] = [] - - func_name_dict[class_name].append(func_name) - - res = { - 'pac_name': pac_name, - 'class_name_list': class_name_list, - 'func_name_dict': func_name_dict, - 'import_pac_name_list': import_pac_name_list - } - return res - - def multi_java_code_parse(self, tree_dict): - ''' - parse multiple java code - > tree_list - < parse_result_dict - ''' - res_dict = {} - for fp, value in tree_dict.items(): - java_code = value['code'] - tree = value['tree'] - try: - res_dict[java_code] = self.single_java_code_parse(tree, fp) - except Exception as e: - logger.debug(java_code) - raise ImportError - - return res_dict - - -if __name__ == '__main__': - java_code_dict = { - 'test': '''package com.theokanning.openai; - -import com.theokanning.openai.client.Utils; - - -public class UtilsTest { - public void testRemoveChar() { - String input = "hello"; - char ch = 'l'; - String expected = "heo"; - String res = Utils.remove(input, ch); - System.out.println(res.equals(expected)); - } -} -''' - } - - jsa = JavaStaticAnalysis() - res = jsa.analyze(java_code_dict) - logger.info(res) - - - - - - diff --git a/coagent/codechat/code_crawler/__init__.py b/coagent/codechat/code_crawler/__init__.py deleted file mode 100644 index 6ddb8ef..0000000 --- a/coagent/codechat/code_crawler/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: __init__.py.py -@time: 2023/11/21 下午2:02 -@desc: -''' -from .zip_crawler import ZipCrawler -from .dir_crawler import DirCrawler - - -__all__ = [ - 'ZipCrawler', - 'DirCrawler' - ] diff --git a/coagent/codechat/code_crawler/dir_crawler.py b/coagent/codechat/code_crawler/dir_crawler.py deleted file mode 100644 index 96dea0c..0000000 --- a/coagent/codechat/code_crawler/dir_crawler.py +++ /dev/null @@ -1,39 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: dir_crawler.py -@time: 2023/11/22 下午2:54 -@desc: -''' -from loguru import logger -import os -import glob - - -class DirCrawler: - @staticmethod - def crawl(path: str, suffix: str): - ''' - read local java file in path - > path: path to crawl, must be absolute path like A/B/C - < dict of java code string - ''' - java_file_list = glob.glob('{path}{sep}**{sep}*.{suffix}'.format(path=path, sep=os.path.sep, suffix=suffix), - recursive=True) - java_code_dict = {} - - logger.info(path) - logger.info('number of file={}'.format(len(java_file_list))) - logger.info(java_file_list) - - for java_file in java_file_list: - with open(java_file, encoding="utf-8") as f: - java_code = ''.join(f.readlines()) - java_code_dict[java_file] = java_code - return java_code_dict - - -if __name__ == '__main__': - path = '/Users/bingxu/Desktop/工作/大模型/chatbot/test_code_repo/middleware-alipay-starters-parent' - suffix = 'java' - DirCrawler.crawl(path, suffix) \ No newline at end of file diff --git a/coagent/codechat/code_crawler/zip_crawler.py b/coagent/codechat/code_crawler/zip_crawler.py deleted file mode 100644 index 8cb968d..0000000 --- a/coagent/codechat/code_crawler/zip_crawler.py +++ /dev/null @@ -1,31 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: zip_crawler.py -@time: 2023/11/21 下午2:02 -@desc: -''' -from loguru import logger - -import zipfile -from coagent.codechat.code_crawler.dir_crawler import DirCrawler - - -class ZipCrawler: - @staticmethod - def crawl(zip_file, output_path, suffix): - ''' - unzip to output_path - @param zip_file: - @param output_path: - @return: - ''' - logger.info(f'output_path={output_path}') - print(f'output_path={output_path}') - with zipfile.ZipFile(zip_file, 'r') as z: - z.extractall(output_path) - - code_dict = DirCrawler.crawl(output_path, suffix) - return code_dict - - diff --git a/coagent/codechat/code_search/__init__.py b/coagent/codechat/code_search/__init__.py deleted file mode 100644 index c9e9dbe..0000000 --- a/coagent/codechat/code_search/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: __init__.py.py -@time: 2023/11/21 下午2:35 -@desc: -''' \ No newline at end of file diff --git a/coagent/codechat/code_search/code_search.py b/coagent/codechat/code_search/code_search.py deleted file mode 100644 index 7e4c505..0000000 --- a/coagent/codechat/code_search/code_search.py +++ /dev/null @@ -1,261 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: code_search.py -@time: 2023/11/21 下午2:35 -@desc: -''' -import json -import time -from loguru import logger -from collections import defaultdict - -from coagent.db_handler.graph_db_handler.nebula_handler import NebulaHandler -from coagent.db_handler.vector_db_handler.chroma_handler import ChromaHandler - -from coagent.codechat.code_search.cypher_generator import CypherGenerator -from coagent.codechat.code_search.tagger import Tagger -from coagent.embeddings.get_embedding import get_embedding -from coagent.llm_models.llm_config import LLMConfig, EmbedConfig - - -# from configs.model_config import EMBEDDING_DEVICE, EMBEDDING_MODEL -# search_by_tag -VERTEX_SCORE = 10 -HISTORY_VERTEX_SCORE = 5 -VERTEX_MERGE_RATIO = 0.5 - -# search_by_description -MAX_DISTANCE = 1000 - - -class CodeSearch: - def __init__(self, llm_config: LLMConfig, nh: NebulaHandler, ch: ChromaHandler, limit: int = 3, - local_graph_file_path: str = ''): - ''' - init - @param nh: NebulaHandler - @param ch: ChromaHandler - @param limit: limit of result - ''' - self.llm_config = llm_config - - self.nh = nh - - if not self.nh: - with open(local_graph_file_path, 'r') as f: - self.graph = json.load(f) - - self.ch = ch - self.limit = limit - - def search_by_tag(self, query: str): - ''' - search_code_res by tag - @param query: str - @return: - ''' - tagger = Tagger() - tag_list = tagger.generate_tag_query(query) - logger.info(f'query tag={tag_list}') - - # get all vertices - vertex_list = self.nh.get_vertices().get('v', []) - vertex_vid_list = [i.as_node().get_id().as_string() for i in vertex_list] - - # update score - vertex_score_dict = defaultdict(lambda: 0) - for vid in vertex_vid_list: - for tag in tag_list: - if tag in vid: - vertex_score_dict[vid] += VERTEX_SCORE - - # merge depend adj score - vertex_score_dict_final = {} - for vertex in vertex_score_dict: - cypher = f'''MATCH (v1)-[e]-(v2) where id(v1) == "{vertex}" RETURN v2''' - cypher_res = self.nh.execute_cypher(cypher, self.nh.space_name) - cypher_res_dict = self.nh.result_to_dict(cypher_res) - - adj_vertex_list = [i.as_node().get_id().as_string() for i in cypher_res_dict.get('v2', [])] - - score = vertex_score_dict.get(vertex, 0) - for adj_vertex in adj_vertex_list: - score += vertex_score_dict.get(adj_vertex, 0) * VERTEX_MERGE_RATIO - - if score > 0: - vertex_score_dict_final[vertex] = score - - # get most prominent package tag - package_score_dict = defaultdict(lambda: 0) - - for vertex, score in vertex_score_dict_final.items(): - if '#' in vertex: - # get class name first - cypher = f'''MATCH (v1:class)-[e:contain]->(v2) WHERE id(v2) == '{vertex}' RETURN id(v1) as id;''' - cypher_res = self.nh.execute_cypher(cypher=cypher, format_res=True) - class_vertices = cypher_res.get('id', []) - if not class_vertices: - continue - - vertex = class_vertices[0].as_string() - - # get package name - cypher = f'''MATCH (v1:package)-[e:contain]->(v2) WHERE id(v2) == '{vertex}' RETURN id(v1) as id;''' - cypher_res = self.nh.execute_cypher(cypher=cypher, format_res=True) - pac_vertices = cypher_res.get('id', []) - if not pac_vertices: - continue - - package = pac_vertices[0].as_string() - package_score_dict[package] += score - - # get respective code - res = [] - package_score_tuple = list(package_score_dict.items()) - package_score_tuple.sort(key=lambda x: x[1], reverse=True) - - ids = [i[0] for i in package_score_tuple] - logger.info(f'ids={ids}') - chroma_res = self.ch.get(ids=ids, include=['metadatas']) - - for vertex, score in package_score_tuple: - index = chroma_res['result']['ids'].index(vertex) - code_text = chroma_res['result']['metadatas'][index]['code_text'] - res.append({ - "vertex": vertex, - "code_text": code_text} - ) - if len(res) >= self.limit: - break - # logger.info(f'retrival code={res}') - return res - - def search_by_tag_by_graph(self, query: str): - ''' - search code by tag with graph - @param query: - @return: - ''' - tagger = Tagger() - tag_list = tagger.generate_tag_query(query) - logger.info(f'query tag={tag_list}') - - # loop to get package node - package_score_dict = {} - for code, structure in self.graph.items(): - score = 0 - for class_name in structure['class_name_list']: - for tag in tag_list: - if tag.lower() in class_name.lower(): - score += 1 - - for func_name_list in structure['func_name_dict'].values(): - for func_name in func_name_list: - for tag in tag_list: - if tag.lower() in func_name.lower(): - score += 1 - package_score_dict[structure['pac_name']] = score - - # get respective code - res = [] - package_score_tuple = list(package_score_dict.items()) - package_score_tuple.sort(key=lambda x: x[1], reverse=True) - - ids = [i[0] for i in package_score_tuple] - logger.info(f'ids={ids}') - chroma_res = self.ch.get(ids=ids, include=['metadatas']) - - # logger.info(chroma_res) - for vertex, score in package_score_tuple: - index = chroma_res['result']['ids'].index(vertex) - code_text = chroma_res['result']['metadatas'][index]['code_text'] - res.append({ - "vertex": vertex, - "code_text": code_text} - ) - if len(res) >= self.limit: - break - # logger.info(f'retrival code={res}') - return res - - def search_by_desciption(self, query: str, engine: str, model_path: str = "text2vec-base-chinese", embedding_device: str = "cpu", embed_config: EmbedConfig=None): - ''' - search by perform sim search - @param query: - @return: - ''' - query = query.replace(',', ',') - query_emb = get_embedding(engine=engine, text_list=[query], model_path=model_path, embedding_device= embedding_device, embed_config=embed_config) - query_emb = query_emb[query] - - query_embeddings = [query_emb] - query_result = self.ch.query(query_embeddings=query_embeddings, n_results=self.limit, - include=['metadatas', 'distances']) - - res = [] - for idx, distance in enumerate(query_result['result']['distances'][0]): - if distance < MAX_DISTANCE: - vertex = query_result['result']['ids'][0][idx] - code_text = query_result['result']['metadatas'][0][idx]['code_text'] - res.append({ - "vertex": vertex, - "code_text": code_text - }) - - return res - - def search_by_cypher(self, query: str): - ''' - search by generating cypher - @param query: - @param engine: - @return: - ''' - cg = CypherGenerator(self.llm_config) - cypher = cg.get_cypher(query) - - if not cypher: - return None - - cypher_res = self.nh.execute_cypher(cypher, self.nh.space_name) - logger.info(f'cypher execution result={cypher_res}') - if not cypher_res.is_succeeded(): - return { - 'cypher': '', - 'cypher_res': '' - } - - res = { - 'cypher': cypher, - 'cypher_res': cypher_res - } - - return res - - -if __name__ == '__main__': - # from configs.server_config import NEBULA_HOST, NEBULA_PORT, NEBULA_USER, NEBULA_PASSWORD, NEBULA_STORAGED_PORT - # from configs.server_config import CHROMA_PERSISTENT_PATH - from coagent.base_configs.env_config import ( - NEBULA_HOST, NEBULA_PORT, NEBULA_USER, NEBULA_PASSWORD, NEBULA_STORAGED_PORT, - CHROMA_PERSISTENT_PATH - ) - codebase_name = 'testing' - - nh = NebulaHandler(host=NEBULA_HOST, port=NEBULA_PORT, username=NEBULA_USER, - password=NEBULA_PASSWORD, space_name=codebase_name) - nh.add_host(NEBULA_HOST, NEBULA_STORAGED_PORT) - time.sleep(0.5) - - ch = ChromaHandler(path=CHROMA_PERSISTENT_PATH, collection_name=codebase_name) - - cs = CodeSearch(nh, ch) - # res = cs.search_by_tag(tag_list=['createFineTuneCompletion', 'OpenAiApi']) - # logger.debug(res) - - # res = cs.search_by_cypher('代码中一共有多少个类', 'openai') - # logger.debug(res) - - res = cs.search_by_desciption('使用不同的HTTP请求类型(GET、POST、DELETE等)来执行不同的操作', 'openai') - logger.debug(res) diff --git a/coagent/codechat/code_search/cypher_generator.py b/coagent/codechat/code_search/cypher_generator.py deleted file mode 100644 index 814839a..0000000 --- a/coagent/codechat/code_search/cypher_generator.py +++ /dev/null @@ -1,82 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: cypher_generator.py -@time: 2023/11/24 上午10:17 -@desc: -''' -from langchain import PromptTemplate -from loguru import logger - -from coagent.llm_models.openai_model import getChatModelFromConfig -from coagent.llm_models.llm_config import LLMConfig -from coagent.utils.postprocess import replace_lt_gt -from langchain.schema import ( - HumanMessage, -) -from langchain.chains.graph_qa.prompts import NGQL_GENERATION_PROMPT, CYPHER_GENERATION_TEMPLATE - -schema = ''' -Node properties: [{'tag': 'package', 'properties': []}, {'tag': 'class', 'properties': []}, {'tag': 'method', 'properties': []}] -Edge properties: [{'edge': 'contain', 'properties': []}, {'edge': 'depend', 'properties': []}] -Relationships: ['(:package)-[:contain]->(:class)', '(:class)-[:contain]->(:method)', '(:package)-[:contain]->(:package)'] -''' - - -class CypherGenerator: - def __init__(self, llm_config: LLMConfig): - self.model = getChatModelFromConfig(llm_config) - NEBULAGRAPH_EXTRA_INSTRUCTIONS = """ - Instructions: - - First, generate cypher then convert it to NebulaGraph Cypher dialect(rather than standard): - 1. it requires explicit label specification only when referring to node properties: v.`Foo`.name - 2. note explicit label specification is not needed for edge properties, so it's e.name instead of e.`Bar`.name - 3. it uses double equals sign for comparison: `==` rather than `=` - 4. only use id(Foo) to get the name of node or edge - ```\n""" - - NGQL_GENERATION_TEMPLATE = CYPHER_GENERATION_TEMPLATE.replace( - "Generate Cypher", "Generate NebulaGraph Cypher" - ).replace("Instructions:", NEBULAGRAPH_EXTRA_INSTRUCTIONS) - - self.NGQL_GENERATION_PROMPT = PromptTemplate( - input_variables=["schema", "question"], template=NGQL_GENERATION_TEMPLATE - ) - - def get_cypher(self, query: str): - ''' - get cypher from query - @param query: - @return: - ''' - content = self.NGQL_GENERATION_PROMPT.format(schema=schema, question=query) - logger.info(content) - ans = '' - message = [HumanMessage(content=content)] - chat_res = self.model.predict_messages(message) - ans = chat_res.content - - ans = replace_lt_gt(ans) - - ans = self.post_process(ans) - return ans - - def post_process(self, cypher_res: str): - ''' - 判断是否为正确的 cypher - @param cypher_res: - @return: - ''' - if '(' not in cypher_res or ')' not in cypher_res: - return '' - - return cypher_res - - -if __name__ == '__main__': - query = '代码库里有哪些函数,返回5个就可以' - cg = CypherGenerator() - - ans = cg.get_cypher(query) - logger.debug(f'ans=\n{ans}') diff --git a/coagent/codechat/code_search/tagger.py b/coagent/codechat/code_search/tagger.py deleted file mode 100644 index b3a2816..0000000 --- a/coagent/codechat/code_search/tagger.py +++ /dev/null @@ -1,39 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: tagger.py -@time: 2023/11/24 下午1:32 -@desc: -''' -import re -from loguru import logger - - -class Tagger: - def __init__(self): - pass - - def generate_tag_query(self, query): - ''' - generate tag from query - ''' - # simple extract english - tag_list = re.findall(r'[a-zA-Z\_\.]+', query) - tag_list = list(set(tag_list)) - tag_list = self.filter_tag_list(tag_list) - return tag_list - - def filter_tag_list(self, tag_list): - ''' - filter out tag - @param tag_list: - @return: - ''' - res = [] - for tag in tag_list: - if tag in ['java', 'python']: - continue - res.append(tag) - return res - - diff --git a/coagent/codechat/codebase_handler/__init__.py b/coagent/codechat/codebase_handler/__init__.py deleted file mode 100644 index bcd2f89..0000000 --- a/coagent/codechat/codebase_handler/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: __init__.py.py -@time: 2023/11/21 下午2:07 -@desc: -''' \ No newline at end of file diff --git a/coagent/codechat/codebase_handler/code_importer.py b/coagent/codechat/codebase_handler/code_importer.py deleted file mode 100644 index c374f39..0000000 --- a/coagent/codechat/codebase_handler/code_importer.py +++ /dev/null @@ -1,214 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: codebase_handler.py -@time: 2023/11/21 下午2:07 -@desc: -''' -import time -import json -import os -from loguru import logger - -from coagent.db_handler.graph_db_handler.nebula_handler import NebulaHandler -from coagent.db_handler.vector_db_handler.chroma_handler import ChromaHandler -from coagent.embeddings.get_embedding import get_embedding -from coagent.llm_models.llm_config import EmbedConfig - - -class CodeImporter: - def __init__(self, codebase_name: str, embed_config: EmbedConfig, nh: NebulaHandler, ch: ChromaHandler, - local_graph_file_path: str): - self.codebase_name = codebase_name - # self.engine = engine - self.embed_config: EmbedConfig = embed_config - self.nh = nh - self.ch = ch - self.local_graph_file_path = local_graph_file_path - - def import_code(self, static_analysis_res: dict, interpretation: dict, do_interpret: bool = True): - ''' - import code to nebula and chroma - @return: - ''' - static_analysis_res = self.filter_out_vertex(static_analysis_res, interpretation) - - if self.nh: - self.analysis_res_to_graph(static_analysis_res) - else: - # persist to local dir - with open(self.local_graph_file_path, 'w') as f: - json.dump(static_analysis_res, f) - - self.interpretation_to_db(static_analysis_res, interpretation, do_interpret) - - def filter_out_vertex(self, static_analysis_res, interpretation): - ''' - filter out nonexist vertices - @param static_analysis_res: - @param interpretation: - @return: - ''' - save_pac_name = set() - for i, j in static_analysis_res.items(): - save_pac_name.add(j['pac_name']) - - for class_name in j['class_name_list']: - save_pac_name.add(class_name) - save_pac_name.update(j['func_name_dict'].get(class_name, [])) - - for _, structure in static_analysis_res.items(): - new_pac_name_list = [] - for i in structure['import_pac_name_list']: - if i in save_pac_name: - new_pac_name_list.append(i) - - structure['import_pac_name_list'] = new_pac_name_list - return static_analysis_res - - def analysis_res_to_graph(self, static_analysis_res): - ''' - transform static_analysis_res to tuple - @param static_analysis_res: - @return: - ''' - vertex_value_dict = { - 'package':{ - 'properties_name': [], - 'values': {} - }, - 'class': { - 'properties_name': [], - 'values': {} - }, - 'method': { - 'properties_name': [], - 'values': {} - }, - } - - edge_value_dict = { - 'contain': { - 'properties_name': [], - 'values': {} - }, - 'depend': { - 'properties_name': [], - 'values': {} - } - } - - for _, structure in static_analysis_res.items(): - pac_name = structure['pac_name'] - vertex_value_dict['package']['values'][pac_name] = [] - - for class_name in structure['class_name_list']: - vertex_value_dict['class']['values'][class_name] = [] - - edge_value_dict['contain']['values'][(pac_name, class_name)] = [] - - for func_name in structure['func_name_dict'].get(class_name, []): - vertex_value_dict['method']['values'][func_name] = [] - - edge_value_dict['contain']['values'][(class_name, func_name)] = [] - - for depend_pac_name in structure['import_pac_name_list']: - vertex_value_dict['package']['values'][depend_pac_name] = [] - - edge_value_dict['depend']['values'][(pac_name, depend_pac_name)] = [] - - # create vertex - for tag_name, value_dict in vertex_value_dict.items(): - res = self.nh.insert_vertex(tag_name, value_dict) - # logger.debug(res.error_msg()) - - # create edge - for tag_name, value_dict in edge_value_dict.items(): - res = self.nh.insert_edge(tag_name, value_dict) - # logger.debug(res.error_msg()) - - return - - def interpretation_to_db(self, static_analysis_res, interpretation, do_interpret, ): - ''' - vectorize interpretation and save to db - @return: - ''' - # if not do_interpret, fake some vector - if do_interpret: - logger.info('start get embedding for interpretion') - interp_list = list(interpretation.values()) - emb = get_embedding(engine=self.embed_config.embed_engine, text_list=interp_list, model_path=self.embed_config.embed_model_path, embedding_device= self.embed_config.model_device, embed_config=self.embed_config) - logger.info('get embedding done') - else: - emb = {i: [0] for i in list(interpretation.values())} - - ids = [] - embeddings = [] - documents = [] - metadatas = [] - - for code_text, interp in interpretation.items(): - if code_text not in static_analysis_res: - continue - - pac_name = static_analysis_res[code_text]['pac_name'] - if pac_name in ids: - continue - - ids.append(pac_name) - documents.append(interp) - - metadatas.append({ - 'code_text': code_text - }) - - embeddings.append(emb[interp]) - - # add documents to chroma - res = self.ch.add_data(ids=ids, embeddings=embeddings, documents=documents, metadatas=metadatas) - # logger.debug(res) - - def init_graph(self): - ''' - init graph - @return: - ''' - res = self.nh.create_space(space_name=self.codebase_name, vid_type='FIXED_STRING(1024)') - # logger.debug(res.error_msg()) - time.sleep(5) - - self.nh.set_space_name(self.codebase_name) - - logger.info(f'space_name={self.nh.space_name}') - # create tag - tag_name = 'package' - prop_dict = {} - res = self.nh.create_tag(tag_name, prop_dict) - # logger.debug(res.error_msg()) - - tag_name = 'class' - prop_dict = {} - res = self.nh.create_tag(tag_name, prop_dict) - # logger.debug(res.error_msg()) - - tag_name = 'method' - prop_dict = {} - res = self.nh.create_tag(tag_name, prop_dict) - # logger.debug(res.error_msg()) - - # create edge type - edge_type_name = 'contain' - prop_dict = {} - res = self.nh.create_edge_type(edge_type_name, prop_dict) - # logger.debug(res.error_msg()) - - # create edge type - edge_type_name = 'depend' - prop_dict = {} - res = self.nh.create_edge_type(edge_type_name, prop_dict) - # logger.debug(res.error_msg()) - - -if __name__ == '__main__': - static_res = {'package com.theokanning.openai.client;\nimport com.theokanning.openai.DeleteResult;\nimport com.theokanning.openai.OpenAiResponse;\nimport com.theokanning.openai.audio.TranscriptionResult;\nimport com.theokanning.openai.audio.TranslationResult;\nimport com.theokanning.openai.billing.BillingUsage;\nimport com.theokanning.openai.billing.Subscription;\nimport com.theokanning.openai.completion.CompletionRequest;\nimport com.theokanning.openai.completion.CompletionResult;\nimport com.theokanning.openai.completion.chat.ChatCompletionRequest;\nimport com.theokanning.openai.completion.chat.ChatCompletionResult;\nimport com.theokanning.openai.edit.EditRequest;\nimport com.theokanning.openai.edit.EditResult;\nimport com.theokanning.openai.embedding.EmbeddingRequest;\nimport com.theokanning.openai.embedding.EmbeddingResult;\nimport com.theokanning.openai.engine.Engine;\nimport com.theokanning.openai.file.File;\nimport com.theokanning.openai.fine_tuning.FineTuningEvent;\nimport com.theokanning.openai.fine_tuning.FineTuningJob;\nimport com.theokanning.openai.fine_tuning.FineTuningJobRequest;\nimport com.theokanning.openai.finetune.FineTuneEvent;\nimport com.theokanning.openai.finetune.FineTuneRequest;\nimport com.theokanning.openai.finetune.FineTuneResult;\nimport com.theokanning.openai.image.CreateImageRequest;\nimport com.theokanning.openai.image.ImageResult;\nimport com.theokanning.openai.model.Model;\nimport com.theokanning.openai.moderation.ModerationRequest;\nimport com.theokanning.openai.moderation.ModerationResult;\nimport io.reactivex.Single;\nimport okhttp3.MultipartBody;\nimport okhttp3.RequestBody;\nimport okhttp3.ResponseBody;\nimport retrofit2.Call;\nimport retrofit2.http.*;\nimport java.time.LocalDate;\npublic interface OpenAiApi {\n @GET("v1/models")\n Single> listModels();\n @GET("/v1/models/{model_id}")\n Single getModel(@Path("model_id") String modelId);\n @POST("/v1/completions")\n Single createCompletion(@Body CompletionRequest request);\n @Streaming\n @POST("/v1/completions")\n Call createCompletionStream(@Body CompletionRequest request);\n @POST("/v1/chat/completions")\n Single createChatCompletion(@Body ChatCompletionRequest request);\n @Streaming\n @POST("/v1/chat/completions")\n Call createChatCompletionStream(@Body ChatCompletionRequest request);\n @Deprecated\n @POST("/v1/engines/{engine_id}/completions")\n Single createCompletion(@Path("engine_id") String engineId, @Body CompletionRequest request);\n @POST("/v1/edits")\n Single createEdit(@Body EditRequest request);\n @Deprecated\n @POST("/v1/engines/{engine_id}/edits")\n Single createEdit(@Path("engine_id") String engineId, @Body EditRequest request);\n @POST("/v1/embeddings")\n Single createEmbeddings(@Body EmbeddingRequest request);\n @Deprecated\n @POST("/v1/engines/{engine_id}/embeddings")\n Single createEmbeddings(@Path("engine_id") String engineId, @Body EmbeddingRequest request);\n @GET("/v1/files")\n Single> listFiles();\n @Multipart\n @POST("/v1/files")\n Single uploadFile(@Part("purpose") RequestBody purpose, @Part MultipartBody.Part file);\n @DELETE("/v1/files/{file_id}")\n Single deleteFile(@Path("file_id") String fileId);\n @GET("/v1/files/{file_id}")\n Single retrieveFile(@Path("file_id") String fileId);\n @Streaming\n @GET("/v1/files/{file_id}/content")\n Single retrieveFileContent(@Path("file_id") String fileId);\n @POST("/v1/fine_tuning/jobs")\n Single createFineTuningJob(@Body FineTuningJobRequest request);\n @GET("/v1/fine_tuning/jobs")\n Single> listFineTuningJobs();\n @GET("/v1/fine_tuning/jobs/{fine_tuning_job_id}")\n Single retrieveFineTuningJob(@Path("fine_tuning_job_id") String fineTuningJobId);\n @POST("/v1/fine_tuning/jobs/{fine_tuning_job_id}/cancel")\n Single cancelFineTuningJob(@Path("fine_tuning_job_id") String fineTuningJobId);\n @GET("/v1/fine_tuning/jobs/{fine_tuning_job_id}/events")\n Single> listFineTuningJobEvents(@Path("fine_tuning_job_id") String fineTuningJobId);\n @Deprecated\n @POST("/v1/fine-tunes")\n Single createFineTune(@Body FineTuneRequest request);\n @POST("/v1/completions")\n Single createFineTuneCompletion(@Body CompletionRequest request);\n @Deprecated\n @GET("/v1/fine-tunes")\n Single> listFineTunes();\n @Deprecated\n @GET("/v1/fine-tunes/{fine_tune_id}")\n Single retrieveFineTune(@Path("fine_tune_id") String fineTuneId);\n @Deprecated\n @POST("/v1/fine-tunes/{fine_tune_id}/cancel")\n Single cancelFineTune(@Path("fine_tune_id") String fineTuneId);\n @Deprecated\n @GET("/v1/fine-tunes/{fine_tune_id}/events")\n Single> listFineTuneEvents(@Path("fine_tune_id") String fineTuneId);\n @DELETE("/v1/models/{fine_tune_id}")\n Single deleteFineTune(@Path("fine_tune_id") String fineTuneId);\n @POST("/v1/images/generations")\n Single createImage(@Body CreateImageRequest request);\n @POST("/v1/images/edits")\n Single createImageEdit(@Body RequestBody requestBody);\n @POST("/v1/images/variations")\n Single createImageVariation(@Body RequestBody requestBody);\n @POST("/v1/audio/transcriptions")\n Single createTranscription(@Body RequestBody requestBody);\n @POST("/v1/audio/translations")\n Single createTranslation(@Body RequestBody requestBody);\n @POST("/v1/moderations")\n Single createModeration(@Body ModerationRequest request);\n @Deprecated\n @GET("v1/engines")\n Single> getEngines();\n @Deprecated\n @GET("/v1/engines/{engine_id}")\n Single getEngine(@Path("engine_id") String engineId);\n /**\n * Account information inquiry: It contains total amount (in US dollars) and other information.\n *\n * @return\n */\n @Deprecated\n @GET("v1/dashboard/billing/subscription")\n Single subscription();\n /**\n * Account call interface consumption amount inquiry.\n * totalUsage = Total amount used by the account (in US cents).\n *\n * @param starDate\n * @param endDate\n * @return Consumption amount information.\n */\n @Deprecated\n @GET("v1/dashboard/billing/usage")\n Single billingUsage(@Query("start_date") LocalDate starDate, @Query("end_date") LocalDate endDate);\n}': {'pac_name': 'com.theokanning.openai.client', 'class_name_list': ['com.theokanning.openai.client.OpenAiApi'], 'func_name_dict': {'com.theokanning.openai.client.OpenAiApi': ['com.theokanning.openai.client.OpenAiApi.listModels', 'com.theokanning.openai.client.OpenAiApi.getModel_String', 'com.theokanning.openai.client.OpenAiApi.createCompletion_CompletionRequest', 'com.theokanning.openai.client.OpenAiApi.createCompletionStream_CompletionRequest', 'com.theokanning.openai.client.OpenAiApi.createChatCompletion_ChatCompletionRequest', 'com.theokanning.openai.client.OpenAiApi.createChatCompletionStream_ChatCompletionRequest', 'com.theokanning.openai.client.OpenAiApi.createCompletion_String_CompletionRequest', 'com.theokanning.openai.client.OpenAiApi.createEdit_EditRequest', 'com.theokanning.openai.client.OpenAiApi.createEdit_String_EditRequest', 'com.theokanning.openai.client.OpenAiApi.createEmbeddings_EmbeddingRequest', 'com.theokanning.openai.client.OpenAiApi.createEmbeddings_String_EmbeddingRequest', 'com.theokanning.openai.client.OpenAiApi.listFiles', 'com.theokanning.openai.client.OpenAiApi.uploadFile_RequestBody_MultipartBody', 'com.theokanning.openai.client.OpenAiApi.deleteFile_String', 'com.theokanning.openai.client.OpenAiApi.retrieveFile_String', 'com.theokanning.openai.client.OpenAiApi.retrieveFileContent_String', 'com.theokanning.openai.client.OpenAiApi.createFineTuningJob_FineTuningJobRequest', 'com.theokanning.openai.client.OpenAiApi.listFineTuningJobs', 'com.theokanning.openai.client.OpenAiApi.retrieveFineTuningJob_String', 'com.theokanning.openai.client.OpenAiApi.cancelFineTuningJob_String', 'com.theokanning.openai.client.OpenAiApi.listFineTuningJobEvents_String', 'com.theokanning.openai.client.OpenAiApi.createFineTune_FineTuneRequest', 'com.theokanning.openai.client.OpenAiApi.createFineTuneCompletion_CompletionRequest', 'com.theokanning.openai.client.OpenAiApi.listFineTunes', 'com.theokanning.openai.client.OpenAiApi.retrieveFineTune_String', 'com.theokanning.openai.client.OpenAiApi.cancelFineTune_String', 'com.theokanning.openai.client.OpenAiApi.listFineTuneEvents_String', 'com.theokanning.openai.client.OpenAiApi.deleteFineTune_String', 'com.theokanning.openai.client.OpenAiApi.createImage_CreateImageRequest', 'com.theokanning.openai.client.OpenAiApi.createImageEdit_RequestBody', 'com.theokanning.openai.client.OpenAiApi.createImageVariation_RequestBody', 'com.theokanning.openai.client.OpenAiApi.createTranscription_RequestBody', 'com.theokanning.openai.client.OpenAiApi.createTranslation_RequestBody', 'com.theokanning.openai.client.OpenAiApi.createModeration_ModerationRequest', 'com.theokanning.openai.client.OpenAiApi.getEngines', 'com.theokanning.openai.client.OpenAiApi.getEngine_String', 'com.theokanning.openai.client.OpenAiApi.subscription', 'com.theokanning.openai.client.OpenAiApi.billingUsage_LocalDate_LocalDate']}, 'import_pac_name_list': ['com.theokanning.openai.DeleteResult', 'com.theokanning.openai.OpenAiResponse', 'com.theokanning.openai.audio.TranscriptionResult', 'com.theokanning.openai.audio.TranslationResult', 'com.theokanning.openai.billing.BillingUsage', 'com.theokanning.openai.billing.Subscription', 'com.theokanning.openai.completion.CompletionRequest', 'com.theokanning.openai.completion.CompletionResult', 'com.theokanning.openai.completion.chat.ChatCompletionRequest', 'com.theokanning.openai.completion.chat.ChatCompletionResult', 'com.theokanning.openai.edit.EditRequest', 'com.theokanning.openai.edit.EditResult', 'com.theokanning.openai.embedding.EmbeddingRequest', 'com.theokanning.openai.embedding.EmbeddingResult', 'com.theokanning.openai.engine.Engine', 'com.theokanning.openai.file.File', 'com.theokanning.openai.fine_tuning.FineTuningEvent', 'com.theokanning.openai.fine_tuning.FineTuningJob', 'com.theokanning.openai.fine_tuning.FineTuningJobRequest', 'com.theokanning.openai.finetune.FineTuneEvent', 'com.theokanning.openai.finetune.FineTuneRequest', 'com.theokanning.openai.finetune.FineTuneResult', 'com.theokanning.openai.image.CreateImageRequest', 'com.theokanning.openai.image.ImageResult', 'com.theokanning.openai.model.Model', 'com.theokanning.openai.moderation.ModerationRequest', 'com.theokanning.openai.moderation.ModerationResult', 'io.reactivex.Single', 'okhttp3.MultipartBody', 'okhttp3.RequestBody', 'okhttp3.ResponseBody', 'retrofit2.Call', 'retrofit2.http', 'java.time.LocalDate']}, '\npackage com.theokanning.openai;\n\n/**\n * OkHttp Interceptor that adds an authorization token header\n * \n * @deprecated Use {@link com.theokanning.openai.client.AuthenticationInterceptor}\n */\n@Deprecated\npublic class AuthenticationInterceptor extends com.theokanning.openai.client.AuthenticationInterceptor {\n\n AuthenticationInterceptor(String token) {\n super(token);\n }\n\n}\n': {'pac_name': 'com.theokanning.openai', 'class_name_list': ['com.theokanning.openai.AuthenticationInterceptor'], 'func_name_dict': {}, 'import_pac_name_list': []}} diff --git a/coagent/codechat/codebase_handler/codebase_handler.py b/coagent/codechat/codebase_handler/codebase_handler.py deleted file mode 100644 index 601cae1..0000000 --- a/coagent/codechat/codebase_handler/codebase_handler.py +++ /dev/null @@ -1,275 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: codebase_handler.py -@time: 2023/11/21 下午2:25 -@desc: -''' -import os -import time -import json -from typing import List -from loguru import logger - -from coagent.base_configs.env_config import ( - NEBULA_HOST, NEBULA_PORT, NEBULA_USER, NEBULA_PASSWORD, NEBULA_STORAGED_PORT, - CHROMA_PERSISTENT_PATH, CB_ROOT_PATH -) - - -from coagent.db_handler.graph_db_handler.nebula_handler import NebulaHandler -from coagent.db_handler.vector_db_handler.chroma_handler import ChromaHandler -from coagent.codechat.code_crawler.zip_crawler import * -from coagent.codechat.code_analyzer.code_analyzer import CodeAnalyzer -from coagent.codechat.codebase_handler.code_importer import CodeImporter -from coagent.codechat.code_search.code_search import CodeSearch -from coagent.llm_models.llm_config import EmbedConfig, LLMConfig - - -class CodeBaseHandler: - def __init__( - self, - codebase_name: str, - code_path: str = '', - language: str = 'java', - crawl_type: str = 'ZIP', - embed_config: EmbedConfig = EmbedConfig(), - llm_config: LLMConfig = LLMConfig(), - use_nh: bool = True, - local_graph_path: str = CB_ROOT_PATH - ): - self.codebase_name = codebase_name - self.code_path = code_path - self.language = language - self.crawl_type = crawl_type - self.embed_config = embed_config - self.llm_config = llm_config - self.local_graph_file_path = local_graph_path + os.sep + f'{self.codebase_name}_graph.json' - - if use_nh: - try: - self.nh = NebulaHandler(host=NEBULA_HOST, port=NEBULA_PORT, username=NEBULA_USER, - password=NEBULA_PASSWORD, space_name=codebase_name) - self.nh.add_host(NEBULA_HOST, NEBULA_STORAGED_PORT) - time.sleep(1) - except: - self.nh = None - try: - with open(self.local_graph_file_path, 'r') as f: - self.graph = json.load(f) - except: - pass - elif local_graph_path: - self.nh = None - try: - with open(self.local_graph_file_path, 'r') as f: - self.graph = json.load(f) - except: - pass - - self.ch = ChromaHandler(path=CHROMA_PERSISTENT_PATH, collection_name=codebase_name) - - def import_code(self, zip_file='', do_interpret=True): - ''' - analyze code and save it to codekg and codedb - @return: - ''' - # init graph to init tag and edge - code_importer = CodeImporter(embed_config=self.embed_config, codebase_name=self.codebase_name, - nh=self.nh, ch=self.ch, local_graph_file_path=self.local_graph_file_path) - if self.nh: - code_importer.init_graph() - time.sleep(5) - - # crawl code - st0 = time.time() - logger.info('start crawl') - code_dict = self.crawl_code(zip_file) - logger.debug('crawl done, rt={}'.format(time.time() - st0)) - - # analyze code - logger.info('start analyze') - st1 = time.time() - code_analyzer = CodeAnalyzer(language=self.language, llm_config=self.llm_config) - static_analysis_res, interpretation = code_analyzer.analyze(code_dict, do_interpret=do_interpret) - logger.debug('analyze done, rt={}'.format(time.time() - st1)) - - # add info to nebula and chroma - st2 = time.time() - code_importer.import_code(static_analysis_res, interpretation, do_interpret=do_interpret) - logger.debug('update codebase done, rt={}'.format(time.time() - st2)) - - # get KG info - if self.nh: - time.sleep(10) # aviod nebula staus didn't complete - stat = self.nh.get_stat() - vertices_num, edges_num = stat['vertices'], stat['edges'] - else: - vertices_num = 0 - edges_num = 0 - - # get chroma info - file_num = self.ch.count()['result'] - - return vertices_num, edges_num, file_num - - def delete_codebase(self, codebase_name: str): - ''' - delete codebase - @param codebase_name: name of codebase - @return: - ''' - if self.nh: - self.nh.drop_space(space_name=codebase_name) - elif self.local_graph_file_path and os.path.isfile(self.local_graph_file_path): - os.remove(self.local_graph_file_path) - - self.ch.delete_collection(collection_name=codebase_name) - - def crawl_code(self, zip_file=''): - ''' - @return: - ''' - if self.language == 'java': - suffix = 'java' - - logger.info(f'crawl_type={self.crawl_type}') - - code_dict = {} - if self.crawl_type.lower() == 'zip': - code_dict = ZipCrawler.crawl(zip_file, output_path=self.code_path, suffix=suffix) - elif self.crawl_type.lower() == 'dir': - code_dict = DirCrawler.crawl(self.code_path, suffix) - - return code_dict - - def search_code(self, query: str, search_type: str, limit: int = 3): - ''' - search code from codebase - @param limit: - @param engine: - @param query: query from user - @param search_type: ['cypher', 'graph', 'vector'] - @return: - ''' - if self.nh: - assert search_type in ['cypher', 'tag', 'description'] - else: - if search_type == 'tag': - search_type = 'tag_by_local_graph' - assert search_type in ['tag_by_local_graph', 'description'] - - code_search = CodeSearch(llm_config=self.llm_config, nh=self.nh, ch=self.ch, limit=limit, - local_graph_file_path=self.local_graph_file_path) - - if search_type == 'cypher': - search_res = code_search.search_by_cypher(query=query) - elif search_type == 'tag': - search_res = code_search.search_by_tag(query=query) - elif search_type == 'description': - search_res = code_search.search_by_desciption( - query=query, engine=self.embed_config.embed_engine, model_path=self.embed_config.embed_model_path, - embedding_device=self.embed_config.model_device, embed_config=self.embed_config) - elif search_type == 'tag_by_local_graph': - search_res = code_search.search_by_tag_by_graph(query=query) - - - context, related_vertice = self.format_search_res(search_res, search_type) - return context, related_vertice - - def format_search_res(self, search_res: str, search_type: str): - ''' - format search_res - @param search_res: - @param search_type: - @return: - ''' - CYPHER_QA_PROMPT = ''' - 执行的 Cypher 是: {cypher} - Cypher 的结果是: {result} - ''' - - if search_type == 'cypher': - context = CYPHER_QA_PROMPT.format(cypher=search_res['cypher'], result=search_res['cypher_res']) - related_vertice = [] - elif search_type == 'tag': - context = '' - related_vertice = [] - for code in search_res: - context = context + code['code_text'] + '\n' - related_vertice.append(code['vertex']) - elif search_type == 'tag_by_local_graph': - context = '' - related_vertice = [] - for code in search_res: - context = context + code['code_text'] + '\n' - related_vertice.append(code['vertex']) - elif search_type == 'description': - context = '' - related_vertice = [] - for code in search_res: - context = context + code['code_text'] + '\n' - related_vertice.append(code['vertex']) - - return context, related_vertice - - def search_vertices(self, vertex_type="class") -> List[str]: - ''' - 通过 method/class 来搜索所有的节点 - ''' - vertices = [] - if self.nh: - vertices = self.nh.get_all_vertices() - vertices = [str(v.as_node().get_id()) for v in vertices["v"] if vertex_type in v.as_node().tags()] - # for v in vertices["v"]: - # logger.debug(f"{v.as_node().get_id()}, {v.as_node().tags()}") - else: - if vertex_type == "class": - vertices = [str(class_name) for code, structure in self.graph.items() for class_name in structure['class_name_list']] - elif vertex_type == "method": - vertices = [ - str(methods_name) - for code, structure in self.graph.items() - for methods_names in structure['func_name_dict'].values() - for methods_name in methods_names - ] - # logger.debug(vertices) - return vertices - - -if __name__ == '__main__': - from configs.model_config import KB_ROOT_PATH, JUPYTER_WORK_PATH - from configs.server_config import SANDBOX_SERVER - - LLM_MODEL = "gpt-3.5-turbo" - llm_config = LLMConfig( - model_name=LLM_MODEL, model_device="cpu", api_key=os.environ["OPENAI_API_KEY"], - api_base_url=os.environ["API_BASE_URL"], temperature=0.3 - ) - src_dir = '/Users/bingxu/Desktop/工作/大模型/chatbot/Codefuse-chatbot-antcode' - embed_config = EmbedConfig( - embed_engine="model", embed_model="text2vec-base-chinese", - embed_model_path=os.path.join(src_dir, "embedding_models/text2vec-base-chinese") - ) - - codebase_name = 'client_local' - code_path = '/Users/bingxu/Desktop/工作/大模型/chatbot/test_code_repo/client' - use_nh = False - local_graph_path = '/Users/bingxu/Desktop/工作/大模型/chatbot/Codefuse-chatbot-antcode/code_base' - CHROMA_PERSISTENT_PATH = '/Users/bingxu/Desktop/工作/大模型/chatbot/Codefuse-chatbot-antcode/data/chroma_data' - - cbh = CodeBaseHandler(codebase_name, code_path, crawl_type='dir', use_nh=use_nh, local_graph_path=local_graph_path, - llm_config=llm_config, embed_config=embed_config) - - # test import code - # cbh.import_code(do_interpret=True) - - # query = '使用不同的HTTP请求类型(GET、POST、DELETE等)来执行不同的操作' - # query = '代码中一共有多少个类' - # query = 'remove 这个函数是用来做什么的' - query = '有没有函数是从字符串中删除指定字符串的功能' - - search_type = 'description' - limit = 2 - res = cbh.search_code(query, search_type, limit) - logger.debug(res) diff --git a/coagent/connector/__init__.py b/coagent/connector/__init__.py deleted file mode 100644 index 70a0b72..0000000 --- a/coagent/connector/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from .configs import PHASE_CONFIGS - - - -PHASE_LIST = list(PHASE_CONFIGS.keys()) - -__all__ = [ - "PHASE_CONFIGS" -] \ No newline at end of file diff --git a/coagent/connector/actions/__init__.py b/coagent/connector/actions/__init__.py deleted file mode 100644 index 13bbc3c..0000000 --- a/coagent/connector/actions/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .base_action import BaseAction - - -__all__ = [ - "BaseAction" -] \ No newline at end of file diff --git a/coagent/connector/actions/base_action.py b/coagent/connector/actions/base_action.py deleted file mode 100644 index d64f97b..0000000 --- a/coagent/connector/actions/base_action.py +++ /dev/null @@ -1,16 +0,0 @@ - -from langchain.schema import BaseRetriever, Document - -class BaseAction: - - - def __init__(self, ): - pass - - def step(self, ): - pass - - def astep(self, ): - pass - - \ No newline at end of file diff --git a/coagent/connector/agents/__init__.py b/coagent/connector/agents/__init__.py deleted file mode 100644 index cc190b1..0000000 --- a/coagent/connector/agents/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .base_agent import BaseAgent -from .react_agent import ReactAgent -from .executor_agent import ExecutorAgent -from .selector_agent import SelectorAgent - -__all__ = [ - "BaseAgent", "ReactAgent", "ExecutorAgent", "SelectorAgent" -] \ No newline at end of file diff --git a/coagent/connector/agents/base_agent.py b/coagent/connector/agents/base_agent.py deleted file mode 100644 index 8afeeab..0000000 --- a/coagent/connector/agents/base_agent.py +++ /dev/null @@ -1,211 +0,0 @@ -from typing import List, Union -import importlib -import re, os -import copy -from loguru import logger - -from langchain.schema import BaseRetriever - -from coagent.connector.schema import ( - Memory, Task, Role, Message, PromptField, LogVerboseEnum -) -from coagent.connector.memory_manager import BaseMemoryManager -from coagent.connector.message_process import MessageUtils -from coagent.llm_models import getExtraModel, LLMConfig, getChatModelFromConfig, EmbedConfig -from coagent.connector.prompt_manager.prompt_manager import PromptManager -from coagent.connector.memory_manager import LocalMemoryManager -from coagent.base_configs.env_config import JUPYTER_WORK_PATH, KB_ROOT_PATH - - -class BaseAgent: - - def __init__( - self, - role: Role, - prompt_config: List[PromptField], - prompt_manager_type: str = "PromptManager", - task: Task = None, - memory: Memory = None, - chat_turn: int = 1, - focus_agents: List[str] = [], - focus_message_keys: List[str] = [], - # - llm_config: LLMConfig = None, - embed_config: EmbedConfig = None, - sandbox_server: dict = {}, - jupyter_work_path: str = JUPYTER_WORK_PATH, - kb_root_path: str = KB_ROOT_PATH, - doc_retrieval: Union[BaseRetriever] = None, - code_retrieval = None, - search_retrieval = None, - log_verbose: str = "0" - ): - - self.task = task - self.role = role - self.sandbox_server = sandbox_server - self.jupyter_work_path = jupyter_work_path - self.kb_root_path = kb_root_path - self.message_utils = MessageUtils(role, sandbox_server, jupyter_work_path, embed_config, llm_config, kb_root_path, doc_retrieval, code_retrieval, search_retrieval, log_verbose) - self.memory = self.init_history(memory) - self.llm_config: LLMConfig = llm_config - self.embed_config: EmbedConfig = embed_config - self.llm = self.create_llm_engine(llm_config=self.llm_config) - self.chat_turn = chat_turn - # - self.focus_agents = focus_agents - self.focus_message_keys = focus_message_keys - # - prompt_manager_module = importlib.import_module("coagent.connector.prompt_manager") - prompt_manager = getattr(prompt_manager_module, prompt_manager_type) - self.prompt_manager: PromptManager = prompt_manager(role_prompt=role.role_prompt, prompt_config=prompt_config) - self.log_verbose = max(os.environ.get("log_verbose", "0"), log_verbose) - - def step(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None) -> Message: - '''agent reponse from multi-message''' - message = None - for message in self.astep(query, history, background, memory_manager): - pass - return message - - def astep(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None) -> Message: - '''agent reponse from multi-message''' - # insert query into memory - query_c = copy.deepcopy(query) - query_c = self.start_action_step(query_c) - - # llm predict - # prompt = self.create_prompt(query_c, self.memory, history, background, memory_pool=memory_manager.current_memory) - if memory_manager is None: - memory_manager = LocalMemoryManager( - unique_name=self.role.role_name, - do_init=True, - kb_root_path = self.kb_root_path, - embed_config=self.embed_config, - llm_config=self.embed_config - ) - memory_manager.append(query) - memory_pool = memory_manager.get_memory_pool(query.user_name) - - prompt = self.prompt_manager.generate_full_prompt( - previous_agent_message=query_c, agent_long_term_memory=self.memory, ui_history=history, chain_summary_messages=background, memory_pool=memory_pool) - content = self.llm.predict(prompt) - - if LogVerboseEnum.ge(LogVerboseEnum.Log2Level, self.log_verbose): - logger.debug(f"{self.role.role_name} prompt: {prompt}") - - if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): - logger.info(f"{self.role.role_name} content: {content}") - - output_message = Message( - user_name=query.user_name, - role_name=self.role.role_name, - role_type="assistant", #self.role.role_type, - role_content=content, - step_content=content, - input_query=query_c.input_query, - tools=query_c.tools, - # parsed_output_list=[query.parsed_output], - customed_kargs=query_c.customed_kargs - ) - - # common parse llm' content to message - output_message = self.message_utils.parser(output_message) - - # action step - output_message, observation_message = self.message_utils.step_router(output_message, history, background, memory_manager=memory_manager) - output_message.parsed_output_list.append(output_message.parsed_output) - if observation_message: - output_message.parsed_output_list.append(observation_message.parsed_output) - - # update self_memory - self.append_history(query_c) - self.append_history(output_message) - - output_message.input_query = output_message.role_content - # end - output_message = self.message_utils.inherit_extrainfo(query, output_message) - output_message = self.end_action_step(output_message) - - # update memory pool - memory_manager.append(output_message) - yield output_message - - def pre_print(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None): - prompt = self.prompt_manager.pre_print( - previous_agent_message=query, agent_long_term_memory=self.memory, ui_history=history, chain_summary_messages=background, memory_pool=memory_manager.current_memory) - title = f"<<<<{self.role.role_name}'s prompt>>>>" - print("#"*len(title) + f"\n{title}\n"+ "#"*len(title)+ f"\n\n{prompt}\n\n") - - def init_history(self, memory: Memory = None) -> Memory: - return Memory(messages=[]) - - def update_history(self, message: Message): - self.memory.append(message) - - def append_history(self, message: Message): - self.memory.append(message) - - def clear_history(self, ): - self.memory.clear() - self.memory = self.init_history() - - def create_llm_engine(self, llm_config: LLMConfig = None, temperature=0.2, stop=None): - return getChatModelFromConfig(llm_config=llm_config) - - def registry_actions(self, actions): - '''registry llm's actions''' - self.action_list = actions - - def start_action_step(self, message: Message) -> Message: - '''do action before agent predict ''' - # action_json = self.start_action() - # message["customed_kargs"]["xx"] = action_json - return message - - def end_action_step(self, message: Message) -> Message: - '''do action after agent predict ''' - # action_json = self.end_action() - # message["customed_kargs"]["xx"] = action_json - return message - - def token_usage(self, ): - '''calculate the usage of token''' - pass - - def select_memory_by_key(self, memory: Memory) -> Memory: - return Memory( - messages=[self.select_message_by_key(message) for message in memory.messages - if self.select_message_by_key(message) is not None] - ) - - def select_memory_by_agent_key(self, memory: Memory) -> Memory: - return Memory( - messages=[self.select_message_by_agent_key(message) for message in memory.messages - if self.select_message_by_agent_key(message) is not None] - ) - - def select_message_by_agent_key(self, message: Message) -> Message: - # assume we focus all agents - if self.focus_agents == []: - return message - return None if message is None or message.role_name not in self.focus_agents else self.select_message_by_key(message) - - def select_message_by_key(self, message: Message) -> Message: - # assume we focus all key contents - if message is None: - return message - - if self.focus_message_keys == []: - return message - - message_c = copy.deepcopy(message) - message_c.parsed_output = {k: v for k,v in message_c.parsed_output.items() if k in self.focus_message_keys} - message_c.parsed_output_list = [{k: v for k,v in parsed_output.items() if k in self.focus_message_keys} for parsed_output in message_c.parsed_output_list] - return message_c - - def get_memory(self, content_key="role_content"): - return self.memory.to_tuple_messages(content_key="step_content") - - def get_memory_str(self, content_key="role_content"): - return "\n".join([": ".join(i) for i in self.memory.to_tuple_messages(content_key="step_content")]) diff --git a/coagent/connector/agents/executor_agent.py b/coagent/connector/agents/executor_agent.py deleted file mode 100644 index 7c714fe..0000000 --- a/coagent/connector/agents/executor_agent.py +++ /dev/null @@ -1,157 +0,0 @@ -from typing import List, Union -import copy -from loguru import logger - -from langchain.schema import BaseRetriever - -from coagent.connector.schema import ( - Memory, Task, Env, Role, Message, ActionStatus, PromptField, LogVerboseEnum -) -from coagent.connector.memory_manager import BaseMemoryManager -from coagent.llm_models import LLMConfig, EmbedConfig -from coagent.connector.memory_manager import LocalMemoryManager -from coagent.base_configs.env_config import JUPYTER_WORK_PATH, KB_ROOT_PATH -from .base_agent import BaseAgent - - -class ExecutorAgent(BaseAgent): - def __init__( - self, - role: Role, - prompt_config: List[PromptField], - prompt_manager_type: str= "PromptManager", - task: Task = None, - memory: Memory = None, - chat_turn: int = 1, - focus_agents: List[str] = [], - focus_message_keys: List[str] = [], - # - llm_config: LLMConfig = None, - embed_config: EmbedConfig = None, - sandbox_server: dict = {}, - jupyter_work_path: str = JUPYTER_WORK_PATH, - kb_root_path: str = KB_ROOT_PATH, - doc_retrieval: Union[BaseRetriever] = None, - code_retrieval = None, - search_retrieval = None, - log_verbose: str = "0" - ): - - super().__init__(role, prompt_config, prompt_manager_type, task, memory, chat_turn, - focus_agents, focus_message_keys, llm_config, embed_config, sandbox_server, - jupyter_work_path, kb_root_path, doc_retrieval, code_retrieval, search_retrieval, log_verbose - ) - self.do_all_task = True # run all tasks - - def astep(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None) -> Message: - '''agent reponse from multi-message''' - # insert query into memory - task_executor_memory = Memory(messages=[]) - # insert query - output_message = Message( - user_name=query.user_name, - role_name=self.role.role_name, - role_type="assistant", #self.role.role_type, - role_content=query.input_query, - step_content="", - input_query=query.input_query, - tools=query.tools, - # parsed_output_list=[query.parsed_output], - customed_kargs=query.customed_kargs - ) - - if memory_manager is None: - memory_manager = LocalMemoryManager( - unique_name=self.role.role_name, - do_init=True, - kb_root_path = self.kb_root_path, - embed_config=self.embed_config, - llm_config=self.embed_config - ) - memory_manager.append(query) - - # self_memory = self.memory if self.do_use_self_memory else None - - plan_step = int(query.parsed_output.get("PLAN_STEP", 0)) - # 如果存在plan字段且plan字段为str的时候 - if "PLAN" not in query.parsed_output or isinstance(query.parsed_output.get("PLAN", []), str) or plan_step >= len(query.parsed_output.get("PLAN", [])): - query_c = copy.deepcopy(query) - query_c = self.start_action_step(query_c) - query_c.parsed_output = {"CURRENT_STEP": query_c.input_query} - task_executor_memory.append(query_c) - for output_message, task_executor_memory in self._arun_step(output_message, query_c, self.memory, history, background, memory_manager, task_executor_memory): - pass - # task_executor_memory.append(query_c) - # content = "the execution step of the plan is exceed the planned scope." - # output_message.parsed_dict = {"Thought": content, "Action Status": "finished", "Action": content} - # task_executor_memory.append(output_message) - - elif "PLAN" in query.parsed_output: - if self.do_all_task: - # run all tasks step by step - for task_content in query.parsed_output["PLAN"][plan_step:]: - # create your llm prompt - query_c = copy.deepcopy(query) - query_c.parsed_output = {"CURRENT_STEP": task_content} - task_executor_memory.append(query_c) - for output_message, task_executor_memory in self._arun_step(output_message, query_c, self.memory, history, background, memory_manager, task_executor_memory): - pass - yield output_message - else: - query_c = copy.deepcopy(query) - query_c = self.start_action_step(query_c) - task_content = query_c.parsed_output["PLAN"][plan_step] - query_c.parsed_output = {"CURRENT_STEP": task_content} - task_executor_memory.append(query_c) - for output_message, task_executor_memory in self._arun_step(output_message, query_c, self.memory, history, background, memory_manager, task_executor_memory): - pass - output_message.parsed_output.update({"CURRENT_STEP": plan_step}) - # update self_memory - self.append_history(query) - self.append_history(output_message) - output_message.input_query = output_message.role_content - # end_action_step - output_message = self.end_action_step(output_message) - # update memory pool - memory_manager.append(output_message) - yield output_message - - def _arun_step(self, output_message: Message, query: Message, self_memory: Memory, - history: Memory, background: Memory, memory_manager: BaseMemoryManager, - task_memory: Memory) -> Union[Message, Memory]: - '''execute the llm predict by created prompt''' - memory_pool = memory_manager.get_memory_pool(query.user_name) - prompt = self.prompt_manager.generate_full_prompt( - previous_agent_message=query, agent_long_term_memory=self_memory, ui_history=history, chain_summary_messages=background, memory_pool=memory_pool, - task_memory=task_memory) - content = self.llm.predict(prompt) - - if LogVerboseEnum.ge(LogVerboseEnum.Log2Level, self.log_verbose): - logger.debug(f"{self.role.role_name} prompt: {prompt}") - - if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): - logger.info(f"{self.role.role_name} content: {content}") - - output_message.role_content = content - output_message.step_content += "\n"+output_message.role_content - output_message = self.message_utils.parser(output_message) - # according the output to choose one action for code_content or tool_content - output_message, observation_message = self.message_utils.step_router(output_message) - # update parserd_output_list - output_message.parsed_output_list.append(output_message.parsed_output) - - react_message = copy.deepcopy(output_message) - task_memory.append(react_message) - if observation_message: - task_memory.append(observation_message) - output_message.parsed_output_list.append(observation_message.parsed_output) - # logger.debug(f"{observation_message.role_name} content: {observation_message.role_content}") - yield output_message, task_memory - - def pre_print(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager = None): - task_memory = Memory(messages=[]) - prompt = self.prompt_manager.pre_print( - previous_agent_message=query, agent_long_term_memory=self.memory, ui_history=history, chain_summary_messages=background, react_memory=None, - memory_pool=memory_manager.current_memory, task_memory=task_memory) - title = f"<<<<{self.role.role_name}'s prompt>>>>" - print("#"*len(title) + f"\n{title}\n"+ "#"*len(title)+ f"\n\n{prompt}\n\n") diff --git a/coagent/connector/agents/react_agent.py b/coagent/connector/agents/react_agent.py deleted file mode 100644 index 1ade305..0000000 --- a/coagent/connector/agents/react_agent.py +++ /dev/null @@ -1,147 +0,0 @@ -from typing import List, Union -import traceback -import copy -from loguru import logger - -from langchain.schema import BaseRetriever - -from coagent.connector.schema import ( - Memory, Task, Env, Role, Message, ActionStatus, PromptField, LogVerboseEnum -) -from coagent.connector.memory_manager import BaseMemoryManager -from coagent.llm_models import LLMConfig, EmbedConfig -from .base_agent import BaseAgent -from coagent.connector.memory_manager import LocalMemoryManager -from coagent.base_configs.env_config import JUPYTER_WORK_PATH, KB_ROOT_PATH - - -class ReactAgent(BaseAgent): - def __init__( - self, - role: Role, - prompt_config: List[PromptField], - prompt_manager_type: str = "PromptManager", - task: Task = None, - memory: Memory = None, - chat_turn: int = 1, - focus_agents: List[str] = [], - focus_message_keys: List[str] = [], - # - llm_config: LLMConfig = None, - embed_config: EmbedConfig = None, - sandbox_server: dict = {}, - jupyter_work_path: str = JUPYTER_WORK_PATH, - kb_root_path: str = KB_ROOT_PATH, - doc_retrieval: Union[BaseRetriever] = None, - code_retrieval = None, - search_retrieval = None, - log_verbose: str = "0" - ): - - super().__init__(role, prompt_config, prompt_manager_type, task, memory, chat_turn, - focus_agents, focus_message_keys, llm_config, embed_config, sandbox_server, - jupyter_work_path, kb_root_path, doc_retrieval, code_retrieval, search_retrieval, log_verbose - ) - - def step(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager = None) -> Message: - '''agent reponse from multi-message''' - for message in self.astep(query, history, background, memory_manager): - pass - return message - - def astep(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager = None) -> Message: - '''agent reponse from multi-message''' - step_nums = copy.deepcopy(self.chat_turn) - react_memory = Memory(messages=[]) - # insert query - output_message = Message( - user_name=query.user_name, - role_name=self.role.role_name, - role_type="assistant", #self.role.role_type, - role_content=query.input_query, - step_content="", - input_query=query.input_query, - tools=query.tools, - # parsed_output_list=[query.parsed_output], - customed_kargs=query.customed_kargs - ) - query_c = copy.deepcopy(query) - query_c = self.start_action_step(query_c) - # if query.parsed_output: - # query_c.parsed_output = {"Question": "\n".join([f"{v}" for k, v in query.parsed_output.items() if k not in ["Action Status"]])} - # else: - # query_c.parsed_output = {"Question": query.input_query} - # react_memory.append(query_c) - # self_memory = self.memory if self.do_use_self_memory else None - idx = 0 - # start to react - while step_nums > 0: - output_message.role_content = output_message.step_content - # prompt = self.create_prompt(query, self.memory, history, background, react_memory, memory_manager.current_memory) - - if memory_manager is None: - memory_manager = LocalMemoryManager( - unique_name=self.role.role_name, - do_init=True, - kb_root_path = self.kb_root_path, - embed_config=self.embed_config, - llm_config=self.embed_config - ) - memory_manager.append(query) - memory_pool = memory_manager.get_memory_pool(query_c.user_name) - - prompt = self.prompt_manager.generate_full_prompt( - previous_agent_message=query_c, agent_long_term_memory=self.memory, ui_history=history, chain_summary_messages=background, react_memory=react_memory, - memory_pool=memory_pool) - try: - content = self.llm.predict(prompt) - except Exception as e: - logger.error(f"error prompt: {prompt}") - raise Exception(traceback.format_exc()) - - output_message.role_content = "\n"+content - output_message.step_content += "\n"+output_message.role_content - yield output_message - - if LogVerboseEnum.ge(LogVerboseEnum.Log2Level, self.log_verbose): - logger.debug(f"{self.role.role_name}, {idx} iteration prompt: {prompt}") - - if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): - logger.info(f"{self.role.role_name}, {idx} iteration step_run: {output_message.role_content}") - - output_message = self.message_utils.parser(output_message) - # when get finished signal can stop early - if output_message.action_status == ActionStatus.FINISHED or output_message.action_status == ActionStatus.STOPPED: - output_message.parsed_output_list.append(output_message.parsed_output) - break - # according the output to choose one action for code_content or tool_content - output_message, observation_message = self.message_utils.step_router(output_message) - output_message.parsed_output_list.append(output_message.parsed_output) - - react_message = copy.deepcopy(output_message) - react_memory.append(react_message) - if observation_message: - react_memory.append(observation_message) - output_message.parsed_output_list.append(observation_message.parsed_output) - # logger.debug(f"{observation_message.role_name} content: {observation_message.role_content}") - idx += 1 - step_nums -= 1 - yield output_message - # react' self_memory saved at last - self.append_history(output_message) - output_message.input_query = query.input_query - # end_action_step, BUG:it may cause slack some information - output_message = self.end_action_step(output_message) - # update memory pool - memory_manager.append(output_message) - yield output_message - - def pre_print(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None): - react_memory = Memory(messages=[]) - prompt = self.prompt_manager.pre_print( - previous_agent_message=query, agent_long_term_memory=self.memory, ui_history=history, chain_summary_messages=background, react_memory=react_memory, - memory_pool=memory_manager.current_memory) - title = f"<<<<{self.role.role_name}'s prompt>>>>" - print("#"*len(title) + f"\n{title}\n"+ "#"*len(title)+ f"\n\n{prompt}\n\n") - - diff --git a/coagent/connector/agents/selector_agent.py b/coagent/connector/agents/selector_agent.py deleted file mode 100644 index 17e6ce5..0000000 --- a/coagent/connector/agents/selector_agent.py +++ /dev/null @@ -1,125 +0,0 @@ -from typing import List, Union -import copy -import random -from loguru import logger - -from langchain.schema import BaseRetriever - -from coagent.connector.schema import ( - Memory, Task, Role, Message, PromptField, LogVerboseEnum -) -from coagent.connector.memory_manager import BaseMemoryManager -from coagent.connector.memory_manager import LocalMemoryManager -from coagent.llm_models import LLMConfig, EmbedConfig -from coagent.base_configs.env_config import JUPYTER_WORK_PATH, KB_ROOT_PATH -from .base_agent import BaseAgent - - -class SelectorAgent(BaseAgent): - - def __init__( - self, - role: Role, - prompt_config: List[PromptField] = None, - prompt_manager_type: str = "PromptManager", - task: Task = None, - memory: Memory = None, - chat_turn: int = 1, - focus_agents: List[str] = [], - focus_message_keys: List[str] = [], - group_agents: List[BaseAgent] = [], - # - llm_config: LLMConfig = None, - embed_config: EmbedConfig = None, - sandbox_server: dict = {}, - jupyter_work_path: str = JUPYTER_WORK_PATH, - kb_root_path: str = KB_ROOT_PATH, - doc_retrieval: Union[BaseRetriever] = None, - code_retrieval = None, - search_retrieval = None, - log_verbose: str = "0" - ): - - super().__init__(role, prompt_config, prompt_manager_type, task, memory, chat_turn, - focus_agents, focus_message_keys, llm_config, embed_config, sandbox_server, - jupyter_work_path, kb_root_path, doc_retrieval, code_retrieval, search_retrieval, log_verbose - ) - self.group_agents = group_agents - - def astep(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None) -> Message: - '''agent reponse from multi-message''' - # insert query into memory - query_c = copy.deepcopy(query) - query_c = self.start_action_step(query_c) - # create your llm prompt - if memory_manager is None: - memory_manager = LocalMemoryManager( - unique_name=self.role.role_name, - do_init=True, - kb_root_path = self.kb_root_path, - embed_config=self.embed_config, - llm_config=self.embed_config - ) - memory_manager.append(query) - memory_pool = memory_manager.get_memory_pool(query_c.user_name) - - prompt = self.prompt_manager.generate_full_prompt( - previous_agent_message=query_c, agent_long_term_memory=self.memory, ui_history=history, chain_summary_messages=background, react_memory=None, - memory_pool=memory_pool, agents=self.group_agents) - content = self.llm.predict(prompt) - - if LogVerboseEnum.ge(LogVerboseEnum.Log2Level, self.log_verbose): - logger.debug(f"{self.role.role_name} prompt: {prompt}") - - if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): - logger.info(f"{self.role.role_name} content: {content}") - - # select agent - select_message = Message( - role_name=self.role.role_name, - role_type="assistant", #self.role.role_type, - role_content=content, - step_content=content, - input_query=query_c.input_query, - tools=query_c.tools, - # parsed_output_list=[query_c.parsed_output] - customed_kargs=query.customed_kargs - ) - # common parse llm' content to message - select_message = self.message_utils.parser(select_message) - select_message.parsed_output_list.append(select_message.parsed_output) - - output_message = None - if select_message.parsed_output.get("Role", "") in [agent.role.role_name for agent in self.group_agents]: - for agent in self.group_agents: - if agent.role.role_name == select_message.parsed_output.get("Role", ""): - break - - # 把除了role以外的信息传给下一个agent - query_c.parsed_output.update({k:v for k,v in select_message.parsed_output.items() if k!="Role"}) - for output_message in agent.astep(query_c, history, background=background, memory_manager=memory_manager): - yield output_message or select_message - # update self_memory - self.append_history(query_c) - self.append_history(output_message) - output_message.input_query = output_message.role_content - # output_message.parsed_output_list.append(output_message.parsed_output) - # - output_message = self.end_action_step(output_message) - # update memory pool - memory_manager.append(output_message) - - select_message.parsed_output = output_message.parsed_output - select_message.spec_parsed_output.update(output_message.spec_parsed_output) - select_message.parsed_output_list.extend(output_message.parsed_output_list) - yield select_message - - def pre_print(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None): - prompt = self.prompt_manager.pre_print( - previous_agent_message=query, agent_long_term_memory=self.memory, ui_history=history, chain_summary_messages=background, react_memory=None, - memory_pool=memory_manager.current_memory, agents=self.group_agents) - title = f"<<<<{self.role.role_name}'s prompt>>>>" - print("#"*len(title) + f"\n{title}\n"+ "#"*len(title)+ f"\n\n{prompt}\n\n") - - for agent in self.group_agents: - agent.pre_print(query=query, history=history, background=background, memory_manager=memory_manager) \ No newline at end of file diff --git a/coagent/connector/antflow/__init__.py b/coagent/connector/antflow/__init__.py deleted file mode 100644 index 633d975..0000000 --- a/coagent/connector/antflow/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .flow import AgentFlow, PhaseFlow, ChainFlow - - - -__all__ = [ - "AgentFlow", "PhaseFlow", "ChainFlow" -] \ No newline at end of file diff --git a/coagent/connector/antflow/flow.py b/coagent/connector/antflow/flow.py deleted file mode 100644 index 0b131f6..0000000 --- a/coagent/connector/antflow/flow.py +++ /dev/null @@ -1,255 +0,0 @@ -import importlib -from typing import List, Union, Dict, Any -from loguru import logger -import os -from langchain.embeddings.base import Embeddings -from langchain.agents import Tool -from langchain.llms.base import BaseLLM, LLM - -from coagent.retrieval.base_retrieval import IMRertrieval -from coagent.llm_models.llm_config import EmbedConfig, LLMConfig -from coagent.connector.phase import BasePhase -from coagent.connector.agents import BaseAgent -from coagent.connector.chains import BaseChain -from coagent.connector.schema import Message, Role, PromptField, ChainConfig -from coagent.tools import toLangchainTools, TOOL_DICT, TOOL_SETS - - -class AgentFlow: - def __init__( - self, - role_name: str, - agent_type: str, - role_type: str = "assistant", - agent_index: int = 0, - role_prompt: str = "", - prompt_config: List[Dict[str, Any]] = [], - prompt_manager_type: str = "PromptManager", - chat_turn: int = 3, - focus_agents: List[str] = [], - focus_messages: List[str] = [], - embeddings: Embeddings = None, - llm: BaseLLM = None, - doc_retrieval: IMRertrieval = None, - code_retrieval: IMRertrieval = None, - search_retrieval: IMRertrieval = None, - **kwargs - ): - self.role_type = role_type - self.role_name = role_name - self.agent_type = agent_type - self.role_prompt = role_prompt - self.agent_index = agent_index - - self.prompt_config = prompt_config - self.prompt_manager_type = prompt_manager_type - - self.chat_turn = chat_turn - self.focus_agents = focus_agents - self.focus_messages = focus_messages - - self.embeddings = embeddings - self.llm = llm - self.doc_retrieval = doc_retrieval - self.code_retrieval = code_retrieval - self.search_retrieval = search_retrieval - # self.build_config() - # self.build_agent() - - def build_config(self, embeddings: Embeddings = None, llm: BaseLLM = None): - self.llm_config = LLMConfig(model_name="test", llm=self.llm or llm) - self.embed_config = EmbedConfig(embed_model="test", langchain_embeddings=self.embeddings or embeddings) - - def build_agent(self, - embeddings: Embeddings = None, llm: BaseLLM = None, - doc_retrieval: IMRertrieval = None, - code_retrieval: IMRertrieval = None, - search_retrieval: IMRertrieval = None, - ): - # 可注册个性化的agent,仅通过start_action和end_action来注册 - # class ExtraAgent(BaseAgent): - # def start_action_step(self, message: Message) -> Message: - # pass - - # def end_action_step(self, message: Message) -> Message: - # pass - # agent_module = importlib.import_module("coagent.connector.agents") - # setattr(agent_module, 'extraAgent', ExtraAgent) - - # 可注册个性化的prompt组装方式, - # class CodeRetrievalPM(PromptManager): - # def handle_code_packages(self, **kwargs) -> str: - # if 'previous_agent_message' not in kwargs: - # return "" - # previous_agent_message: Message = kwargs['previous_agent_message'] - # # 由于两个agent共用了同一个manager,所以临时性处理 - # vertices = previous_agent_message.customed_kargs.get("RelatedVerticesRetrivalRes", {}).get("vertices", []) - # return ", ".join([str(v) for v in vertices]) - - # prompt_manager_module = importlib.import_module("coagent.connector.prompt_manager") - # setattr(prompt_manager_module, 'CodeRetrievalPM', CodeRetrievalPM) - - # agent实例化 - agent_module = importlib.import_module("coagent.connector.agents") - baseAgent: BaseAgent = getattr(agent_module, self.agent_type) - role = Role( - role_type=self.agent_type, role_name=self.role_name, - agent_type=self.agent_type, role_prompt=self.role_prompt, - ) - - self.build_config(embeddings, llm) - self.agent = baseAgent( - role=role, - prompt_config = [PromptField(**config) for config in self.prompt_config], - prompt_manager_type=self.prompt_manager_type, - chat_turn=self.chat_turn, - focus_agents=self.focus_agents, - focus_message_keys=self.focus_messages, - llm_config=self.llm_config, - embed_config=self.embed_config, - doc_retrieval=doc_retrieval or self.doc_retrieval, - code_retrieval=code_retrieval or self.code_retrieval, - search_retrieval=search_retrieval or self.search_retrieval, - ) - - - -class ChainFlow: - def __init__( - self, - chain_name: str, - chain_index: int = 0, - agent_flows: List[AgentFlow] = [], - chat_turn: int = 5, - do_checker: bool = False, - embeddings: Embeddings = None, - llm: BaseLLM = None, - doc_retrieval: IMRertrieval = None, - code_retrieval: IMRertrieval = None, - search_retrieval: IMRertrieval = None, - # chain_type: str = "BaseChain", - **kwargs - ): - self.agent_flows = sorted(agent_flows, key=lambda x:x.agent_index) - self.chat_turn = chat_turn - self.do_checker = do_checker - self.chain_name = chain_name - self.chain_index = chain_index - self.chain_type = "BaseChain" - - self.embeddings = embeddings - self.llm = llm - - self.doc_retrieval = doc_retrieval - self.code_retrieval = code_retrieval - self.search_retrieval = search_retrieval - # self.build_config() - # self.build_chain() - - def build_config(self, embeddings: Embeddings = None, llm: BaseLLM = None): - self.llm_config = LLMConfig(model_name="test", llm=self.llm or llm) - self.embed_config = EmbedConfig(embed_model="test", langchain_embeddings=self.embeddings or embeddings) - - def build_chain(self, - embeddings: Embeddings = None, llm: BaseLLM = None, - doc_retrieval: IMRertrieval = None, - code_retrieval: IMRertrieval = None, - search_retrieval: IMRertrieval = None, - ): - # chain 实例化 - chain_module = importlib.import_module("coagent.connector.chains") - baseChain: BaseChain = getattr(chain_module, self.chain_type) - - agent_names = [agent_flow.role_name for agent_flow in self.agent_flows] - chain_config = ChainConfig(chain_name=self.chain_name, agents=agent_names, do_checker=self.do_checker, chat_turn=self.chat_turn) - - # agent 实例化 - self.build_config(embeddings, llm) - for agent_flow in self.agent_flows: - agent_flow.build_agent(embeddings, llm) - - self.chain = baseChain( - chain_config, - [agent_flow.agent for agent_flow in self.agent_flows], - embed_config=self.embed_config, - llm_config=self.llm_config, - doc_retrieval=doc_retrieval or self.doc_retrieval, - code_retrieval=code_retrieval or self.code_retrieval, - search_retrieval=search_retrieval or self.search_retrieval, - ) - -class PhaseFlow: - def __init__( - self, - phase_name: str, - chain_flows: List[ChainFlow], - embeddings: Embeddings = None, - llm: BaseLLM = None, - tools: List[Tool] = [], - doc_retrieval: IMRertrieval = None, - code_retrieval: IMRertrieval = None, - search_retrieval: IMRertrieval = None, - **kwargs - ): - self.phase_name = phase_name - self.chain_flows = sorted(chain_flows, key=lambda x:x.chain_index) - self.phase_type = "BasePhase" - self.tools = tools - - self.embeddings = embeddings - self.llm = llm - - self.doc_retrieval = doc_retrieval - self.code_retrieval = code_retrieval - self.search_retrieval = search_retrieval - # self.build_config() - self.build_phase() - - def __call__(self, params: dict) -> str: - - # tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) - # query_content = "帮我确认下127.0.0.1这个服务器的在10点是否存在异常,请帮我判断一下" - try: - logger.info(f"params: {params}") - query_content = params.get("query") or params.get("input") - search_type = params.get("search_type") - query = Message( - role_name="human", role_type="user", tools=self.tools, - role_content=query_content, input_query=query_content, origin_query=query_content, - cb_search_type=search_type, - ) - # phase.pre_print(query) - output_message, output_memory = self.phase.step(query) - output_content = "\n\n".join((output_memory.to_str_messages(return_all=True, content_key="parsed_output_list").split("\n\n")[1:])) or output_message.role_content - return output_content - except Exception as e: - logger.exception(e) - return f"Error {e}" - - def build_config(self, embeddings: Embeddings = None, llm: BaseLLM = None): - self.llm_config = LLMConfig(model_name="test", llm=self.llm or llm) - self.embed_config = EmbedConfig(embed_model="test", langchain_embeddings=self.embeddings or embeddings) - - def build_phase(self, embeddings: Embeddings = None, llm: BaseLLM = None): - # phase 实例化 - phase_module = importlib.import_module("coagent.connector.phase") - basePhase: BasePhase = getattr(phase_module, self.phase_type) - - # chain 实例化 - self.build_config(self.embeddings or embeddings, self.llm or llm) - os.environ["log_verbose"] = "2" - for chain_flow in self.chain_flows: - chain_flow.build_chain( - self.embeddings or embeddings, self.llm or llm, - self.doc_retrieval, self.code_retrieval, self.search_retrieval - ) - - self.phase: BasePhase = basePhase( - phase_name=self.phase_name, - chains=[chain_flow.chain for chain_flow in self.chain_flows], - embed_config=self.embed_config, - llm_config=self.llm_config, - doc_retrieval=self.doc_retrieval, - code_retrieval=self.code_retrieval, - search_retrieval=self.search_retrieval - ) diff --git a/coagent/connector/chains/__init__.py b/coagent/connector/chains/__init__.py deleted file mode 100644 index 8d3676c..0000000 --- a/coagent/connector/chains/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .base_chain import BaseChain - -__all__ = [ - "BaseChain" -] \ No newline at end of file diff --git a/coagent/connector/chains/base_chain.py b/coagent/connector/chains/base_chain.py deleted file mode 100644 index 7dc986c..0000000 --- a/coagent/connector/chains/base_chain.py +++ /dev/null @@ -1,130 +0,0 @@ -from typing import List, Tuple, Union -from loguru import logger -import copy, os - -from langchain.schema import BaseRetriever - -from coagent.connector.agents import BaseAgent -from coagent.connector.schema import ( - Memory, Role, Message, ActionStatus, ChainConfig, - load_role_configs -) -from coagent.connector.memory_manager import BaseMemoryManager -from coagent.connector.message_process import MessageUtils -from coagent.llm_models.llm_config import LLMConfig, EmbedConfig -from coagent.base_configs.env_config import JUPYTER_WORK_PATH, KB_ROOT_PATH -from coagent.connector.configs.agent_config import AGETN_CONFIGS -role_configs = load_role_configs(AGETN_CONFIGS) - - -class BaseChain: - def __init__( - self, - chainConfig: ChainConfig, - agents: List[BaseAgent], - # chat_turn: int = 1, - # do_checker: bool = False, - sandbox_server: dict = {}, - jupyter_work_path: str = JUPYTER_WORK_PATH, - kb_root_path: str = KB_ROOT_PATH, - llm_config: LLMConfig = LLMConfig(), - embed_config: EmbedConfig = None, - doc_retrieval: Union[BaseRetriever] = None, - code_retrieval = None, - search_retrieval = None, - log_verbose: str = "0" - ) -> None: - self.chainConfig = chainConfig - self.agents: List[BaseAgent] = agents - self.chat_turn = chainConfig.chat_turn - self.do_checker = chainConfig.do_checker - self.sandbox_server = sandbox_server - self.jupyter_work_path = jupyter_work_path - self.llm_config = llm_config - self.log_verbose = max(os.environ.get("log_verbose", "0"), log_verbose) - self.checker = BaseAgent(role=role_configs["checker"].role, - prompt_config=role_configs["checker"].prompt_config, - task = None, memory = None, - llm_config=llm_config, embed_config=embed_config, - sandbox_server=sandbox_server, jupyter_work_path=jupyter_work_path, - kb_root_path=kb_root_path, - doc_retrieval=doc_retrieval, code_retrieval=code_retrieval, - search_retrieval=search_retrieval - ) - self.messageUtils = MessageUtils(None, sandbox_server, self.jupyter_work_path, embed_config, llm_config, kb_root_path, doc_retrieval, code_retrieval, search_retrieval, log_verbose) - # all memory created by agent until instance deleted - self.global_memory = Memory(messages=[]) - - def step(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager = None) -> Message: - '''execute chain''' - for output_message, local_memory in self.astep(query, history, background, memory_manager): - pass - return output_message, local_memory - - def pre_print(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager = None) -> Message: - '''execute chain''' - for agent in self.agents: - agent.pre_print(query, history, background=background, memory_manager=memory_manager) - - def astep(self, query: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager = None) -> Tuple[Message, Memory]: - '''execute chain''' - local_memory = Memory(messages=[]) - input_message = copy.deepcopy(query) - step_nums = copy.deepcopy(self.chat_turn) - check_message = None - - # if input_message not in memory_manager: - # memory_manager.append(input_message) - - self.global_memory.append(input_message) - # local_memory.append(input_message) - while step_nums > 0: - for agent in self.agents: - for output_message in agent.astep(input_message, history, background=background, memory_manager=memory_manager): - # logger.debug(f"local_memory {local_memory + output_message}") - yield output_message, local_memory + output_message - output_message = self.messageUtils.inherit_extrainfo(input_message, output_message) - # according the output to choose one action for code_content or tool_content - # output_message = self.messageUtils.parser(output_message) - yield output_message, local_memory + output_message - # output_message = self.step_router(output_message) - input_message = output_message - self.global_memory.append(output_message) - - local_memory.append(output_message) - # when get finished signal can stop early - if output_message.action_status == ActionStatus.FINISHED or output_message.action_status == ActionStatus.STOPPED: - action_status = False - break - if output_message.action_status == ActionStatus.FINISHED: - break - - if self.do_checker and self.chat_turn > 1: - for check_message in self.checker.astep(query, background=local_memory, memory_manager=memory_manager): - pass - check_message = self.messageUtils.parser(check_message) - check_message = self.messageUtils.inherit_extrainfo(output_message, check_message) - # logger.debug(f"{self.checker.role.role_name}: {check_message.role_content}") - - if check_message.action_status == ActionStatus.FINISHED: - self.global_memory.append(check_message) - break - step_nums -= 1 - # - output_message = check_message or output_message # 返回chain和checker的结果 - output_message.input_query = query.input_query # chain和chain之间消息通信不改变问题 - yield output_message, local_memory - - def get_memory(self, content_key="role_content") -> Memory: - memory = self.global_memory - return memory.to_tuple_messages(content_key=content_key) - - def get_memory_str(self, content_key="role_content") -> Memory: - memory = self.global_memory - return "\n".join([": ".join(i) for i in memory.to_tuple_messages(content_key=content_key)]) - - def get_agents_memory(self, content_key="role_content"): - return [agent.get_memory(content_key=content_key) for agent in self.agents] - - def get_agents_memory_str(self, content_key="role_content"): - return "************".join([f"{agent.role.role_name}\n" + agent.get_memory_str(content_key=content_key) for agent in self.agents]) \ No newline at end of file diff --git a/coagent/connector/chains/chains.py b/coagent/connector/chains/chains.py deleted file mode 100644 index 50d71bc..0000000 --- a/coagent/connector/chains/chains.py +++ /dev/null @@ -1,12 +0,0 @@ -from typing import List -from loguru import logger -from coagent.connector.agents import BaseAgent -from .base_chain import BaseChain - - - - -class ExecutorRefineChain(BaseChain): - - def __init__(self, agents: List[BaseAgent], do_code_exec: bool = False) -> None: - super().__init__(agents, do_code_exec) diff --git a/coagent/connector/configs/__init__.py b/coagent/connector/configs/__init__.py deleted file mode 100644 index 2cb4d69..0000000 --- a/coagent/connector/configs/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from .agent_config import AGETN_CONFIGS -from .chain_config import CHAIN_CONFIGS -from .phase_config import PHASE_CONFIGS -from .prompt_config import * - -__all__ = [ - "AGETN_CONFIGS", "CHAIN_CONFIGS", "PHASE_CONFIGS", - "BASE_PROMPT_CONFIGS", "EXECUTOR_PROMPT_CONFIGS", "SELECTOR_PROMPT_CONFIGS", "BASE_NOTOOLPROMPT_CONFIGS", - "CODE2DOC_GROUP_PROMPT_CONFIGS", "CODE2DOC_PROMPT_CONFIGS", "CODE2TESTS_PROMPT_CONFIGS" - ] \ No newline at end of file diff --git a/coagent/connector/configs/agent_config.py b/coagent/connector/configs/agent_config.py deleted file mode 100644 index 927e0a9..0000000 --- a/coagent/connector/configs/agent_config.py +++ /dev/null @@ -1,330 +0,0 @@ -from enum import Enum -from .prompts import * -# from .prompts import ( -# REACT_PROMPT_INPUT, CHECK_PROMPT_INPUT, EXECUTOR_PROMPT_INPUT, CONTEXT_PROMPT_INPUT, QUERY_CONTEXT_PROMPT_INPUT,PLAN_PROMPT_INPUT, -# RECOGNIZE_INTENTION_PROMPT, -# CHECKER_TEMPLATE_PROMPT, -# CONV_SUMMARY_PROMPT, -# QA_PROMPT, CODE_QA_PROMPT, QA_TEMPLATE_PROMPT, -# EXECUTOR_TEMPLATE_PROMPT, -# REFINE_TEMPLATE_PROMPT, -# SELECTOR_AGENT_TEMPLATE_PROMPT, -# PLANNER_TEMPLATE_PROMPT, GENERAL_PLANNER_PROMPT, DATA_PLANNER_PROMPT, TOOL_PLANNER_PROMPT, -# PRD_WRITER_METAGPT_PROMPT, DESIGN_WRITER_METAGPT_PROMPT, TASK_WRITER_METAGPT_PROMPT, CODE_WRITER_METAGPT_PROMPT, -# REACT_TEMPLATE_PROMPT, -# REACT_TOOL_PROMPT, REACT_CODE_PROMPT, REACT_TOOL_AND_CODE_PLANNER_PROMPT, REACT_TOOL_AND_CODE_PROMPT -# ) -from .prompt_config import * -# BASE_PROMPT_CONFIGS, EXECUTOR_PROMPT_CONFIGS, SELECTOR_PROMPT_CONFIGS, BASE_NOTOOLPROMPT_CONFIGS - - - -class AgentType: - REACT = "ReactAgent" - EXECUTOR = "ExecutorAgent" - ONE_STEP = "BaseAgent" - DEFAULT = "BaseAgent" - SELECTOR = "SelectorAgent" - - - -AGETN_CONFIGS = { - "baseGroup": { - "role": { - "role_prompt": SELECTOR_AGENT_TEMPLATE_PROMPT, - "role_type": "assistant", - "role_name": "baseGroup", - "role_desc": "", - "agent_type": "SelectorAgent" - }, - "prompt_config": SELECTOR_PROMPT_CONFIGS, - "group_agents": ["tool_react", "code_react"], - "chat_turn": 1, - }, - "checker": { - "role": { - "role_prompt": CHECKER_TEMPLATE_PROMPT, - "role_type": "assistant", - "role_name": "checker", - "role_desc": "", - "agent_type": "BaseAgent" - }, - "prompt_config": BASE_PROMPT_CONFIGS, - "chat_turn": 1, - }, - "conv_summary": { - "role": { - "role_prompt": CONV_SUMMARY_PROMPT, - "role_type": "assistant", - "role_name": "conv_summary", - "role_desc": "", - "agent_type": "BaseAgent" - }, - "prompt_config": BASE_PROMPT_CONFIGS, - "chat_turn": 1, - }, - "general_planner": { - "role": { - "role_prompt": PLANNER_TEMPLATE_PROMPT, - "role_type": "assistant", - "role_name": "general_planner", - "role_desc": "", - "agent_type": "BaseAgent" - }, - "prompt_config": BASE_PROMPT_CONFIGS, - "chat_turn": 1, - }, - "executor": { - "role": { - "role_prompt": EXECUTOR_TEMPLATE_PROMPT, - "role_type": "assistant", - "role_name": "executor", - "role_desc": "", - "agent_type": "ExecutorAgent", - }, - "prompt_config": EXECUTOR_PROMPT_CONFIGS, - "stop": "\n**Observation:**", - "chat_turn": 1, - }, - "base_refiner": { - "role": { - "role_prompt": REFINE_TEMPLATE_PROMPT, - "role_type": "assistant", - "role_name": "base_refiner", - "role_desc": "", - "agent_type": "BaseAgent" - }, - "prompt_config": BASE_PROMPT_CONFIGS, - "chat_turn": 1, - }, - "planner": { - "role": { - "role_prompt": DATA_PLANNER_PROMPT, - "role_type": "assistant", - "role_name": "planner", - "role_desc": "", - "agent_type": "BaseAgent" - }, - "prompt_config": BASE_PROMPT_CONFIGS, - "chat_turn": 1, - }, - "intention_recognizer": { - "role": { - "role_prompt": RECOGNIZE_INTENTION_PROMPT, - "role_type": "assistant", - "role_name": "intention_recognizer", - "role_desc": "", - "agent_type": "BaseAgent" - }, - "prompt_config": BASE_PROMPT_CONFIGS, - "chat_turn": 1, - }, - "tool_planner": { - "role": { - "role_prompt": TOOL_PLANNER_PROMPT, - "role_type": "assistant", - "role_name": "tool_planner", - "role_desc": "", - "agent_type": "BaseAgent" - }, - "prompt_config": BASE_PROMPT_CONFIGS, - "chat_turn": 1, - }, - "tool_and_code_react": { - "role": { - "role_prompt": REACT_TOOL_AND_CODE_PROMPT, - "role_type": "assistant", - "role_name": "tool_and_code_react", - "role_desc": "", - "agent_type": "ReactAgent", - }, - "prompt_config": BASE_PROMPT_CONFIGS, - "stop": "\n**Observation:**", - "chat_turn": 7, - }, - "tool_and_code_planner": { - "role": { - "role_prompt": REACT_TOOL_AND_CODE_PLANNER_PROMPT, - "role_type": "assistant", - "role_name": "tool_and_code_planner", - "role_desc": "", - "agent_type": "BaseAgent" - }, - "prompt_config": BASE_PROMPT_CONFIGS, - "chat_turn": 1, - }, - "tool_react": { - "role": { - "role_prompt": REACT_TOOL_PROMPT, - "role_type": "assistant", - "role_name": "tool_react", - "role_desc": "", - "agent_type": "ReactAgent" - }, - "prompt_config": BASE_PROMPT_CONFIGS, - "chat_turn": 5, - "stop": "\n**Observation:**" - }, - "code_react": { - "role": { - "role_prompt": REACT_CODE_PROMPT, - "role_type": "assistant", - "role_name": "code_react", - "role_desc": "", - "agent_type": "ReactAgent" - }, - "prompt_config": BASE_NOTOOLPROMPT_CONFIGS, - "chat_turn": 5, - "stop": "\n**Observation:**" - }, - "qaer": { - "role": { - "role_prompt": QA_TEMPLATE_PROMPT, - "role_type": "assistant", - "role_name": "qaer", - "role_desc": "", - "agent_type": "BaseAgent" - }, - "prompt_config": BASE_PROMPT_CONFIGS, - "chat_turn": 1, - }, - "code_qaer": { - "role": { - "role_prompt": CODE_QA_PROMPT, - "role_type": "assistant", - "role_name": "code_qaer", - "role_desc": "", - "agent_type": "BaseAgent" - }, - "prompt_config": BASE_PROMPT_CONFIGS, - "chat_turn": 1, - }, - "searcher": { - "role": { - "role_prompt": QA_TEMPLATE_PROMPT, - "role_type": "assistant", - "role_name": "searcher", - "role_desc": "", - "agent_type": "BaseAgent" - }, - "prompt_config": BASE_PROMPT_CONFIGS, - "chat_turn": 1, - }, - "metaGPT_PRD": { - "role": { - "role_prompt": PRD_WRITER_METAGPT_PROMPT, - "role_type": "assistant", - "role_name": "metaGPT_PRD", - "role_desc": "", - "agent_type": "BaseAgent" - }, - "prompt_config": BASE_PROMPT_CONFIGS, - "chat_turn": 1, - "focus_agents": [], - "focus_message_keys": [], - }, - - "metaGPT_DESIGN": { - "role": { - "role_prompt": DESIGN_WRITER_METAGPT_PROMPT, - "role_type": "assistant", - "role_name": "metaGPT_DESIGN", - "role_desc": "", - "agent_type": "BaseAgent" - }, - "prompt_config": BASE_PROMPT_CONFIGS, - "chat_turn": 1, - "focus_agents": ["metaGPT_PRD"], - "focus_message_keys": [], - }, - "metaGPT_TASK": { - "role": { - "role_prompt": TASK_WRITER_METAGPT_PROMPT, - "role_type": "assistant", - "role_name": "metaGPT_TASK", - "role_desc": "", - "agent_type": "BaseAgent" - }, - "prompt_config": BASE_PROMPT_CONFIGS, - "chat_turn": 1, - "focus_agents": ["metaGPT_DESIGN"], - "focus_message_keys": [], - }, - "metaGPT_CODER": { - "role": { - "role_prompt": CODE_WRITER_METAGPT_PROMPT, - "role_type": "assistant", - "role_name": "metaGPT_CODER", - "role_desc": "", - "agent_type": "ExecutorAgent" - }, - "prompt_config": EXECUTOR_PROMPT_CONFIGS, - "chat_turn": 1, - "focus_agents": ["metaGPT_DESIGN", "metaGPT_TASK"], - "focus_message_keys": [], - }, - "class2Docer": { - "role": { - "role_prompt": Class2Doc_PROMPT, - "role_type": "assistant", - "role_name": "class2Docer", - "role_desc": "", - "agent_type": "CodeGenDocer" - }, - "prompt_config": CODE2DOC_PROMPT_CONFIGS, - "prompt_manager_type": "Code2DocPM", - "chat_turn": 1, - "focus_agents": [], - "focus_message_keys": [], - }, - "func2Docer": { - "role": { - "role_prompt": Func2Doc_PROMPT, - "role_type": "assistant", - "role_name": "func2Docer", - "role_desc": "", - "agent_type": "CodeGenDocer" - }, - "prompt_config": CODE2DOC_PROMPT_CONFIGS, - "prompt_manager_type": "Code2DocPM", - "chat_turn": 1, - "focus_agents": [], - "focus_message_keys": [], - }, - "code2DocsGrouper": { - "role": { - "role_prompt": Code2DocGroup_PROMPT, - "role_type": "assistant", - "role_name": "code2DocsGrouper", - "role_desc": "", - "agent_type": "SelectorAgent" - }, - "prompt_config": CODE2DOC_GROUP_PROMPT_CONFIGS, - "group_agents": ["class2Docer", "func2Docer"], - "chat_turn": 1, - }, - "Code2TestJudger": { - "role": { - "role_prompt": judgeCode2Tests_PROMPT, - "role_type": "assistant", - "role_name": "Code2TestJudger", - "role_desc": "", - "agent_type": "CodeRetrieval" - }, - "prompt_config": CODE2TESTS_PROMPT_CONFIGS, - "prompt_manager_type": "CodeRetrievalPM", - "chat_turn": 1, - }, - "code2Tests": { - "role": { - "role_prompt": code2Tests_PROMPT, - "role_type": "assistant", - "role_name": "code2Tests", - "role_desc": "", - "agent_type": "CodeRetrieval" - }, - "prompt_config": CODE2TESTS_PROMPT_CONFIGS, - "prompt_manager_type": "CodeRetrievalPM", - "chat_turn": 1, - }, -} \ No newline at end of file diff --git a/coagent/connector/configs/agent_prompt/design_writer.yaml b/coagent/connector/configs/agent_prompt/design_writer.yaml deleted file mode 100644 index e1135ef..0000000 --- a/coagent/connector/configs/agent_prompt/design_writer.yaml +++ /dev/null @@ -1,99 +0,0 @@ -You are a Architect, named Bob, your goal is Design a concise, usable, complete python system, and the constraint is Try to specify good open source tools as much as possible. - -# Context -## Original Requirements: -Create a snake game. - -## Product Goals: -Develop a highly addictive and engaging snake game. -Provide a user-friendly and intuitive user interface. -Implement various levels and challenges to keep the players entertained. -## User Stories: -As a user, I want to be able to control the snake's movement using arrow keys or touch gestures. -As a user, I want to see my score and progress displayed on the screen. -As a user, I want to be able to pause and resume the game at any time. -As a user, I want to be challenged with different obstacles and levels as I progress. -As a user, I want to have the option to compete with other players and compare my scores. -## Competitive Analysis: -Python Snake Game: A simple snake game implemented in Python with basic features and limited levels. -Snake.io: A multiplayer online snake game with competitive gameplay and high engagement. -Slither.io: Another multiplayer online snake game with a larger player base and addictive gameplay. -Snake Zone: A mobile snake game with various power-ups and challenges. -Snake Mania: A classic snake game with modern graphics and smooth controls. -Snake Rush: A fast-paced snake game with time-limited challenges. -Snake Master: A snake game with unique themes and customizable snakes. - -## Requirement Analysis: -The product should be a highly addictive and engaging snake game with a user-friendly interface. It should provide various levels and challenges to keep the players entertained. The game should have smooth controls and allow the users to compete with each other. - -## Requirement Pool: -``` -[ - ["Implement different levels with increasing difficulty", "P0"], - ["Allow users to control the snake using arrow keys or touch gestures", "P0"], - ["Display the score and progress on the screen", "P1"], - ["Provide an option to pause and resume the game", "P1"], - ["Integrate leaderboards to enable competition among players", "P2"] -] -``` -## UI Design draft: -The game will have a simple and clean interface. The main screen will display the snake, obstacles, and the score. The snake's movement can be controlled using arrow keys or touch gestures. There will be buttons to pause and resume the game. The level and difficulty will be indicated on the screen. The design will have a modern and visually appealing style with smooth animations. - -## Anything UNCLEAR: -There are no unclear points. - -## Format example ---- -## Implementation approach -We will ... - -## Python package name -```python -"snake_game" -``` - -## File list -```python -[ - "main.py", -] -``` - -## Data structures and interface definitions -```mermaid -classDiagram - class Game{ - +int score - } - ... - Game "1" -- "1" Food: has -``` - -## Program call flow -```mermaid -sequenceDiagram - participant M as Main - ... - G->>M: end game -``` - -## Anything UNCLEAR -The requirement is clear to me. ---- ------ -Role: You are an architect; the goal is to design a SOTA PEP8-compliant python system; make the best use of good open source tools -Requirement: Fill in the following missing information based on the context, note that all sections are response with code form separately -Max Output: 8192 chars or 2048 tokens. Try to use them up. -Attention: Use '##' to split sections, not '#', and '## ' SHOULD WRITE BEFORE the code and triple quote. - -## Implementation approach: Provide as Plain text. Analyze the difficult points of the requirements, select the appropriate open-source framework. - -## Python package name: Provide as Python str with python triple quoto, concise and clear, characters only use a combination of all lowercase and underscores - -## File list: Provided as Python list[str], the list of ONLY REQUIRED files needed to write the program(LESS IS MORE!). Only need relative paths, comply with PEP8 standards. ALWAYS write a main.py or app.py here - -## Data structures and interface definitions: Use mermaid classDiagram code syntax, including classes (INCLUDING __init__ method) and functions (with type annotations), CLEARLY MARK the RELATIONSHIPS between classes, and comply with PEP8 standards. The data structures SHOULD BE VERY DETAILED and the API should be comprehensive with a complete design. - -## Program call flow: Use sequenceDiagram code syntax, COMPLETE and VERY DETAILED, using CLASSES AND API DEFINED ABOVE accurately, covering the CRUD AND INIT of each object, SYNTAX MUST BE CORRECT. - -## Anything UNCLEAR: Provide as Plain text. Make clear here. \ No newline at end of file diff --git a/coagent/connector/configs/agent_prompt/prd_writer.yaml b/coagent/connector/configs/agent_prompt/prd_writer.yaml deleted file mode 100644 index 0aec402..0000000 --- a/coagent/connector/configs/agent_prompt/prd_writer.yaml +++ /dev/null @@ -1,101 +0,0 @@ -You are a Product Manager, named Alice, your goal is Efficiently create a successful product, and the constraint is . - -# Context -## Original Requirements -Create a snake game. - -## Search Information -### Search Results - -### Search Summary - -## mermaid quadrantChart code syntax example. DONT USE QUOTO IN CODE DUE TO INVALID SYNTAX. Replace the with REAL COMPETITOR NAME -```mermaid -quadrantChart - title Reach and engagement of campaigns - x-axis Low Reach --> High Reach - y-axis Low Engagement --> High Engagement - quadrant-1 We should expand - quadrant-2 Need to promote - quadrant-3 Re-evaluate - quadrant-4 May be improved - "Campaign: A": [0.3, 0.6] - "Campaign B": [0.45, 0.23] - "Campaign C": [0.57, 0.69] - "Campaign D": [0.78, 0.34] - "Campaign E": [0.40, 0.34] - "Campaign F": [0.35, 0.78] - "Our Target Product": [0.5, 0.6] -``` - -## Format example ---- -## Original Requirements -The boss ... - -## Product Goals -```python -[ - "Create a ...", -] -``` - -## User Stories -```python -[ - "As a user, ...", -] -``` - -## Competitive Analysis -```python -[ - "Python Snake Game: ...", -] -``` - -## Competitive Quadrant Chart -```mermaid -quadrantChart - title Reach and engagement of campaigns - ... - "Our Target Product": [0.6, 0.7] -``` - -## Requirement Analysis -The product should be a ... - -## Requirement Pool -```python -[ - ["End game ...", "P0"] -] -``` - -## UI Design draft -Give a basic function description, and a draft - -## Anything UNCLEAR -There are no unclear points. ---- ------ -Role: You are a professional product manager; the goal is to design a concise, usable, efficient product -Requirements: According to the context, fill in the following missing information, note that each sections are returned in Python code triple quote form seperatedly. If the requirements are unclear, ensure minimum viability and avoid excessive design -ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. AND '## ' SHOULD WRITE BEFORE the code and triple quote. Output carefully referenced "Format example" in format. - -## Original Requirements: Provide as Plain text, place the polished complete original requirements here - -## Product Goals: Provided as Python list[str], up to 3 clear, orthogonal product goals. If the requirement itself is simple, the goal should also be simple - -## User Stories: Provided as Python list[str], up to 5 scenario-based user stories, If the requirement itself is simple, the user stories should also be less - -## Competitive Analysis: Provided as Python list[str], up to 7 competitive product analyses, consider as similar competitors as possible - -## Competitive Quadrant Chart: Use mermaid quadrantChart code syntax. up to 14 competitive products. Translation: Distribute these competitor scores evenly between 0 and 1, trying to conform to a normal distribution centered around 0.5 as much as possible. - -## Requirement Analysis: Provide as Plain text. Be simple. LESS IS MORE. Make your requirements less dumb. Delete the parts unnessasery. - -## Requirement Pool: Provided as Python list[list[str], the parameters are requirement description, priority(P0/P1/P2), respectively, comply with PEP standards; no more than 5 requirements and consider to make its difficulty lower - -## UI Design draft: Provide as Plain text. Be simple. Describe the elements and functions, also provide a simple style description and layout description. -## Anything UNCLEAR: Provide as Plain text. Make clear here. \ No newline at end of file diff --git a/coagent/connector/configs/agent_prompt/review_code.yaml b/coagent/connector/configs/agent_prompt/review_code.yaml deleted file mode 100644 index 32567d8..0000000 --- a/coagent/connector/configs/agent_prompt/review_code.yaml +++ /dev/null @@ -1,177 +0,0 @@ - -NOTICE -Role: You are a professional software engineer, and your main task is to review the code. You need to ensure that the code conforms to the PEP8 standards, is elegantly designed and modularized, easy to read and maintain, and is written in Python 3.9 (or in another programming language). -ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example". - -## Code Review: Based on the following context and code, and following the check list, Provide key, clear, concise, and specific code modification suggestions, up to 5. -``` -1. Check 0: Is the code implemented as per the requirements? -2. Check 1: Are there any issues with the code logic? -3. Check 2: Does the existing code follow the "Data structures and interface definitions"? -4. Check 3: Is there a function in the code that is omitted or not fully implemented that needs to be implemented? -5. Check 4: Does the code have unnecessary or lack dependencies? -``` - -## Rewrite Code: point.py Base on "Code Review" and the source code, rewrite code with triple quotes. Do your utmost to optimize THIS SINGLE FILE. ------ -# Context -## Implementation approach -For the snake game, we can use the Pygame library, which is an open-source and easy-to-use library for game development in Python. Pygame provides a simple and efficient way to handle graphics, sound, and user input, making it suitable for developing a snake game. - -## Python package name -``` -"snake_game" -``` -## File list -```` -[ - "main.py", -] -``` -## Data structures and interface definitions -``` -classDiagram - class Game: - -int score - -bool paused - +__init__() - +start_game() - +handle_input(key: int) - +update_game() - +draw_game() - +game_over() - - class Snake: - -list[Point] body - -Point dir - -bool alive - +__init__(start_pos: Point) - +move() - +change_direction(dir: Point) - +grow() - +get_head() -> Point - +get_body() -> list[Point] - +is_alive() -> bool - - class Point: - -int x - -int y - +__init__(x: int, y: int) - +set_coordinate(x: int, y: int) - +get_coordinate() -> tuple[int, int] - - class Food: - -Point pos - -bool active - +__init__() - +generate_new_food() - +get_position() -> Point - +is_active() -> bool - - Game "1" -- "1" Snake: contains - Game "1" -- "1" Food: has -``` - -## Program call flow -``` -sequenceDiagram - participant M as Main - participant G as Game - participant S as Snake - participant F as Food - - M->>G: Start game - G->>G: Initialize game - loop - M->>G: Handle user input - G->>S: Handle input - G->>F: Check if snake eats food - G->>S: Update snake movement - G->>G: Check game over condition - G->>G: Update score - G->>G: Draw game - M->>G: Update display - end - G->>G: Game over -``` -## Required Python third-party packages -``` -""" -pygame==2.0.1 -""" -``` -## Required Other language third-party packages -``` -""" -No third-party packages required for other languages. -""" -``` - -## Logic Analysis -``` -[ - ["main.py", "Main"], - ["game.py", "Game"], - ["snake.py", "Snake"], - ["point.py", "Point"], - ["food.py", "Food"] -] -``` -## Task list -``` -[ - "point.py", - "food.py", - "snake.py", - "game.py", - "main.py" -] -``` -## Shared Knowledge -``` -""" -The 'point.py' module contains the implementation of the Point class, which represents a point in a 2D coordinate system. - -The 'food.py' module contains the implementation of the Food class, which represents the food in the game. - -The 'snake.py' module contains the implementation of the Snake class, which represents the snake in the game. - -The 'game.py' module contains the implementation of the Game class, which manages the game logic. - -The 'main.py' module is the entry point of the application and starts the game. -""" -``` -## Anything UNCLEAR -We need to clarify the main entry point of the application and ensure that all required third-party libraries are properly initialized. - -## Code: point.py -``` -class Point: - def __init__(self, x: int, y: int): - self.x = x - self.y = y - - def set_coordinate(self, x: int, y: int): - self.x = x - self.y = y - - def get_coordinate(self) -> tuple[int, int]: - return self.x, self.y -``` ------ - -## Format example ------ -## Code Review -1. The code ... -2. ... -3. ... -4. ... -5. ... - -## Rewrite Code: point.py -```python -## point.py -... -``` ------ diff --git a/coagent/connector/configs/agent_prompt/task_write.yaml b/coagent/connector/configs/agent_prompt/task_write.yaml deleted file mode 100644 index faf2c2c..0000000 --- a/coagent/connector/configs/agent_prompt/task_write.yaml +++ /dev/null @@ -1,148 +0,0 @@ -You are a Project Manager, named Eve, your goal isImprove team efficiency and deliver with quality and quantity, and the constraint is . - -# Context -## Implementation approach -For the snake game, we can use the Pygame library, which is an open-source and easy-to-use library for game development in Python. Pygame provides a simple and efficient way to handle graphics, sound, and user input, making it suitable for developing a snake game. - -## Python package name -``` -"snake_game" -``` -## File list -```` -[ - "main.py", - "game.py", - "snake.py", - "food.py" -] -``` -## Data structures and interface definitions -``` -classDiagram - class Game{ - -int score - -bool game_over - +start_game() : void - +end_game() : void - +update() : void - +draw() : void - +handle_events() : void - } - class Snake{ - -list[Tuple[int, int]] body - -Tuple[int, int] direction - +move() : void - +change_direction(direction: Tuple[int, int]) : void - +is_collision() : bool - +grow() : void - +draw() : void - } - class Food{ - -Tuple[int, int] position - +generate() : void - +draw() : void - } - class Main{ - -Game game - +run() : void - } - Game "1" -- "1" Snake: contains - Game "1" -- "1" Food: has - Main "1" -- "1" Game: has -``` -## Program call flow -``` -sequenceDiagram - participant M as Main - participant G as Game - participant S as Snake - participant F as Food - - M->G: run() - G->G: start_game() - G->G: handle_events() - G->G: update() - G->G: draw() - G->G: end_game() - - G->S: move() - S->S: change_direction() - S->S: is_collision() - S->S: grow() - S->S: draw() - - G->F: generate() - F->F: draw() -``` -## Anything UNCLEAR -The design and implementation of the snake game are clear based on the given requirements. - -## Format example ---- -## Required Python third-party packages -```python -""" -flask==1.1.2 -bcrypt==3.2.0 -""" -``` - -## Required Other language third-party packages -```python -""" -No third-party ... -""" -``` - -## Full API spec -```python -""" -openapi: 3.0.0 -... -description: A JSON object ... -""" -``` - -## Logic Analysis -```python -[ - ["game.py", "Contains ..."], -] -``` - -## Task list -```python -[ - "game.py", -] -``` - -## Shared Knowledge -```python -""" -'game.py' contains ... -""" -``` - -## Anything UNCLEAR -We need ... how to start. ---- ------ -Role: You are a project manager; the goal is to break down tasks according to PRD/technical design, give a task list, and analyze task dependencies to start with the prerequisite modules -Requirements: Based on the context, fill in the following missing information, note that all sections are returned in Python code triple quote form seperatedly. Here the granularity of the task is a file, if there are any missing files, you can supplement them -Attention: Use '##' to split sections, not '#', and '## ' SHOULD WRITE BEFORE the code and triple quote. - -## Required Python third-party packages: Provided in requirements.txt format - -## Required Other language third-party packages: Provided in requirements.txt format - -## Full API spec: Use OpenAPI 3.0. Describe all APIs that may be used by both frontend and backend. - -## Logic Analysis: Provided as a Python list[list[str]. the first is filename, the second is class/method/function should be implemented in this file. Analyze the dependencies between the files, which work should be done first - -## Task list: Provided as Python list[str]. Each str is a filename, the more at the beginning, the more it is a prerequisite dependency, should be done first - -## Shared Knowledge: Anything that should be public like utils' functions, config's variables details that should make clear first. - -## Anything UNCLEAR: Provide as Plain text. Make clear here. For example, don't forget a main entry. don't forget to init 3rd party libs. \ No newline at end of file diff --git a/coagent/connector/configs/agent_prompt/write_code.yaml b/coagent/connector/configs/agent_prompt/write_code.yaml deleted file mode 100644 index 4193b8b..0000000 --- a/coagent/connector/configs/agent_prompt/write_code.yaml +++ /dev/null @@ -1,147 +0,0 @@ -NOTICE -Role: You are a professional engineer; the main goal is to write PEP8 compliant, elegant, modular, easy to read and maintain Python 3.9 code (but you can also use other programming language) -ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example". - -## Code: snake.py Write code with triple quoto, based on the following list and context. -1. Do your best to implement THIS ONLY ONE FILE. ONLY USE EXISTING API. IF NO API, IMPLEMENT IT. -2. Requirement: Based on the context, implement one following code file, note to return only in code form, your code will be part of the entire project, so please implement complete, reliable, reusable code snippets -3. Attention1: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. -4. Attention2: YOU MUST FOLLOW "Data structures and interface definitions". DONT CHANGE ANY DESIGN. -5. Think before writing: What should be implemented and provided in this document? -6. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE. -7. Do not use public member functions that do not exist in your design. - ------ -# Context -## Implementation approach -For the snake game, we can use the Pygame library, which is an open-source and easy-to-use library for game development in Python. Pygame provides a simple and efficient way to handle graphics, sound, and user input, making it suitable for developing a snake game. - -## Python package name -``` -"snake_game" -``` -## File list -```` -[ - "main.py", - "game.py", - "snake.py", - "food.py" -] -``` -## Data structures and interface definitions -``` -classDiagram - class Game{ - -int score - -bool game_over - +start_game() : void - +end_game() : void - +update() : void - +draw() : void - +handle_events() : void - } - class Snake{ - -list[Tuple[int, int]] body - -Tuple[int, int] direction - +move() : void - +change_direction(direction: Tuple[int, int]) : void - +is_collision() : bool - +grow() : void - +draw() : void - } - class Food{ - -Tuple[int, int] position - +generate() : void - +draw() : void - } - class Main{ - -Game game - +run() : void - } - Game "1" -- "1" Snake: contains - Game "1" -- "1" Food: has - Main "1" -- "1" Game: has -``` -## Program call flow -``` -sequenceDiagram - participant M as Main - participant G as Game - participant S as Snake - participant F as Food - - M->G: run() - G->G: start_game() - G->G: handle_events() - G->G: update() - G->G: draw() - G->G: end_game() - - G->S: move() - S->S: change_direction() - S->S: is_collision() - S->S: grow() - S->S: draw() - - G->F: generate() - F->F: draw() -``` -## Anything UNCLEAR -The design and implementation of the snake game are clear based on the given requirements. - -## Required Python third-party packages -``` -""" -pygame==2.0.1 -""" -``` -## Required Other language third-party packages -``` -""" -No third-party packages required for other languages. -""" -``` - -## Logic Analysis -``` -[ - ["main.py", "Main"], - ["game.py", "Game"], - ["snake.py", "Snake"], - ["food.py", "Food"] -] -``` -## Task list -``` -[ - "snake.py", - "food.py", - "game.py", - "main.py" -] -``` -## Shared Knowledge -``` -""" -'game.py' contains the main logic for the snake game, including starting the game, handling user input, updating the game state, and drawing the game state. - -'snake.py' contains the logic for the snake, including moving the snake, changing its direction, checking for collisions, growing the snake, and drawing the snake. - -'food.py' contains the logic for the food, including generating a new food position and drawing the food. - -'main.py' initializes the game and runs the game loop. -""" -``` -## Anything UNCLEAR -We need to clarify the main entry point of the application and ensure that all required third-party libraries are properly initialized. - ------ -## Format example ------ -## Code: snake.py -```python -## snake.py -... -``` ------ \ No newline at end of file diff --git a/coagent/connector/configs/chain_config.py b/coagent/connector/configs/chain_config.py deleted file mode 100644 index 587ef95..0000000 --- a/coagent/connector/configs/chain_config.py +++ /dev/null @@ -1,143 +0,0 @@ -from enum import Enum -# from .prompts import PLANNER_TEMPLATE_PROMPT - - - -CHAIN_CONFIGS = { - "chatChain": { - "chain_name": "chatChain", - "chain_type": "BaseChain", - "agents": ["qaer"], - "chat_turn": 1, - "do_checker": False, - "chain_prompt": "" - }, - "docChatChain": { - "chain_name": "docChatChain", - "chain_type": "BaseChain", - "agents": ["qaer"], - "chat_turn": 1, - "do_checker": False, - "chain_prompt": "" - }, - "searchChatChain": { - "chain_name": "searchChatChain", - "chain_type": "BaseChain", - "agents": ["searcher"], - "chat_turn": 1, - "do_checker": False, - "chain_prompt": "" - }, - "codeChatChain": { - "chain_name": "codehChatChain", - "chain_type": "BaseChain", - "agents": ["code_qaer"], - "chat_turn": 1, - "do_checker": False, - "chain_prompt": "" - }, - "toolReactChain": { - "chain_name": "toolReactChain", - "chain_type": "BaseChain", - "agents": ["tool_planner", "tool_react"], - "chat_turn": 2, - "do_checker": True, - "chain_prompt": "" - }, - "codePlannerChain": { - "chain_name": "codePlannerChain", - "chain_type": "BaseChain", - "agents": ["planner"], - "chat_turn": 1, - "do_checker": True, - "chain_prompt": "" - }, - "codeReactChain": { - "chain_name": "codeReactChain", - "chain_type": "BaseChain", - "agents": ["code_react"], - "chat_turn": 6, - "do_checker": True, - "chain_prompt": "" - }, - "codeToolPlanChain": { - "chain_name": "codeToolPlanChain", - "chain_type": "BaseChain", - "agents": ["tool_and_code_planner"], - "chat_turn": 1, - "do_checker": False, - "chain_prompt": "" - }, - "codeToolReactChain": { - "chain_name": "codeToolReactChain", - "chain_type": "BaseChain", - "agents": ["tool_and_code_react"], - "chat_turn": 3, - "do_checker": True, - "chain_prompt": "" - }, - "planChain": { - "chain_name": "planChain", - "chain_type": "BaseChain", - "agents": ["general_planner"], - "chat_turn": 1, - "do_checker": False, - "chain_prompt": "" - }, - "executorChain": { - "chain_name": "executorChain", - "chain_type": "BaseChain", - "agents": ["executor"], - "chat_turn": 1, - "do_checker": True, - "chain_prompt": "" - }, - "executorRefineChain": { - "chain_name": "executorRefineChain", - "chain_type": "BaseChain", - "agents": ["executor", "base_refiner"], - "chat_turn": 3, - "do_checker": True, - "chain_prompt": "" - }, - "metagptChain": { - "chain_name": "metagptChain", - "chain_type": "BaseChain", - "agents": ["metaGPT_PRD", "metaGPT_DESIGN", "metaGPT_TASK", "metaGPT_CODER"], - "chat_turn": 1, - "do_checker": False, - "chain_prompt": "" - }, - "baseGroupChain": { - "chain_name": "baseGroupChain", - "chain_type": "BaseChain", - "agents": ["baseGroup"], - "chat_turn": 1, - "do_checker": False, - "chain_prompt": "" - }, - "codeChatXXChain": { - "chain_name": "codeChatXXChain", - "chain_type": "BaseChain", - "agents": ["codeChat1", "codeChat2"], - "chat_turn": 1, - "do_checker": False, - "chain_prompt": "" - }, - "code2DocsGroupChain": { - "chain_name": "code2DocsGroupChain", - "chain_type": "BaseChain", - "agents": ["code2DocsGrouper"], - "chat_turn": 1, - "do_checker": False, - "chain_prompt": "" - }, - "code2TestsChain": { - "chain_name": "code2TestsChain", - "chain_type": "BaseChain", - "agents": ["Code2TestJudger", "code2Tests"], - "chat_turn": 1, - "do_checker": False, - "chain_prompt": "" - } -} diff --git a/coagent/connector/configs/phase_config.py b/coagent/connector/configs/phase_config.py deleted file mode 100644 index 0e88611..0000000 --- a/coagent/connector/configs/phase_config.py +++ /dev/null @@ -1,74 +0,0 @@ -PHASE_CONFIGS = { - "chatPhase": { - "phase_name": "chatPhase", - "phase_type": "BasePhase", - "chains": ["chatChain"], - "do_summary": False, - "do_search": False, - "do_doc_retrieval": False, - "do_code_retrieval": False, - "do_tool_retrieval": False, - "do_using_tool": False - }, - "docChatPhase": { - "phase_name": "docChatPhase", - "phase_type": "BasePhase", - "chains": ["docChatChain"], - "do_doc_retrieval": True, - }, - "searchChatPhase": { - "phase_name": "searchChatPhase", - "phase_type": "BasePhase", - "chains": ["searchChatChain"], - "do_search": True, - }, - "codeChatPhase": { - "phase_name": "codeChatPhase", - "phase_type": "BasePhase", - "chains": ["codeChatChain"], - "do_code_retrieval": True, - }, - "toolReactPhase": { - "phase_name": "toolReactPhase", - "phase_type": "BasePhase", - "chains": ["toolReactChain"], - "do_using_tool": True - }, - "codeReactPhase": { - "phase_name": "codeReactPhase", - "phase_type": "BasePhase", - # "chains": ["codePlannerChain", "codeReactChain"], - "chains": ["planChain", "codeReactChain"], - }, - "codeToolReactPhase": { - "phase_name": "codeToolReactPhase", - "phase_type": "BasePhase", - "chains": ["codeToolPlanChain", "codeToolReactChain"], - "do_using_tool": True - }, - "baseTaskPhase": { - "phase_name": "baseTaskPhase", - "phase_type": "BasePhase", - "chains": ["planChain", "executorChain"], - }, - "metagpt_code_devlop": { - "phase_name": "metagpt_code_devlop", - "phase_type": "BasePhase", - "chains": ["metagptChain",], - }, - "baseGroupPhase": { - "phase_name": "baseGroupPhase", - "phase_type": "BasePhase", - "chains": ["baseGroupChain"], - }, - "code2DocsGroup": { - "phase_name": "code2DocsGroup", - "phase_type": "BasePhase", - "chains": ["code2DocsGroupChain"], - }, - "code2Tests": { - "phase_name": "code2Tests", - "phase_type": "BasePhase", - "chains": ["code2TestsChain"], - } -} diff --git a/coagent/connector/configs/prompt_config.py b/coagent/connector/configs/prompt_config.py deleted file mode 100644 index 4fd049e..0000000 --- a/coagent/connector/configs/prompt_config.py +++ /dev/null @@ -1,80 +0,0 @@ -BASE_PROMPT_CONFIGS = [ - {"field_name": 'agent_profile', "function_name": 'handle_agent_profile', "is_context": False}, - {"field_name": 'tool_information',"function_name": 'handle_tool_data', "is_context": False}, - {"field_name": 'context_placeholder', "function_name": '', "is_context": True}, - {"field_name": 'reference_documents', "function_name": 'handle_doc_info'}, - {"field_name": 'session_records', "function_name": 'handle_session_records'}, - {"field_name": 'task_records', "function_name": 'handle_task_records'}, - {"field_name": 'output_format', "function_name": 'handle_output_format', 'title': 'Response Output Format', "is_context": False}, - {"field_name": 'begin!!!', "function_name": 'handle_response', "is_context": False, "omit_if_empty": False} - ] - -BASE_NOTOOLPROMPT_CONFIGS = [ - {"field_name": 'agent_profile', "function_name": 'handle_agent_profile', "is_context": False}, - {"field_name": 'context_placeholder', "function_name": '', "is_context": True}, - {"field_name": 'reference_documents', "function_name": 'handle_doc_info'}, - {"field_name": 'session_records', "function_name": 'handle_session_records'}, - {"field_name": 'output_format', "function_name": 'handle_output_format', 'title': 'Response Output Format', "is_context": False}, - {"field_name": 'begin!!!', "function_name": 'handle_response', "is_context": False, "omit_if_empty": False} - ] - -EXECUTOR_PROMPT_CONFIGS = [ - {"field_name": 'agent_profile', "function_name": 'handle_agent_profile', "is_context": False}, - {"field_name": 'tool_information',"function_name": 'handle_tool_data', "is_context": False}, - {"field_name": 'context_placeholder', "function_name": '', "is_context": True}, - {"field_name": 'reference_documents', "function_name": 'handle_doc_info'}, - {"field_name": 'session_records', "function_name": 'handle_session_records'}, - {"field_name": 'task_records', "function_name": 'handle_task_records'}, - {"field_name": 'current_plan', "function_name": 'handle_current_plan'}, - {"field_name": 'output_format', "function_name": 'handle_output_format', 'title': 'Response Output Format', "is_context": False}, - {"field_name": 'begin!!!', "function_name": 'handle_response', "is_context": False, "omit_if_empty": False} - ] - -SELECTOR_PROMPT_CONFIGS = [ - {"field_name": 'agent_profile', "function_name": 'handle_agent_profile', "is_context": False}, - {"field_name": 'tool_information',"function_name": 'handle_tool_data', "is_context": False}, - {"field_name": 'agent_infomation', "function_name": 'handle_agent_data', "is_context": False, "omit_if_empty": False}, - {"field_name": 'context_placeholder', "function_name": '', "is_context": True}, - {"field_name": 'reference_documents', "function_name": 'handle_doc_info'}, - {"field_name": 'session_records', "function_name": 'handle_session_records'}, - {"field_name": 'current_plan', "function_name": 'handle_current_plan'}, - {"field_name": 'output_format', "function_name": 'handle_output_format', 'title': 'Response Output Format', "is_context": False}, - {"field_name": 'begin!!!', "function_name": 'handle_response', "is_context": False, "omit_if_empty": False} - ] - - -CODE2DOC_GROUP_PROMPT_CONFIGS = [ - {"field_name": 'agent_profile', "function_name": 'handle_agent_profile', "is_context": False}, - {"field_name": 'agent_infomation', "function_name": 'handle_agent_data', "is_context": False, "omit_if_empty": False}, - # {"field_name": 'tool_information',"function_name": 'handle_tool_data', "is_context": False}, - {"field_name": 'context_placeholder', "function_name": '', "is_context": True}, - # {"field_name": 'reference_documents', "function_name": 'handle_doc_info'}, - {"field_name": 'session_records', "function_name": 'handle_session_records'}, - {"field_name": 'Specific Objective', "function_name": 'handle_specific_objective'}, - {"field_name": 'Code Snippet', "function_name": 'handle_code_snippet'}, - {"field_name": 'output_format', "function_name": 'handle_output_format', 'title': 'Response Output Format', "is_context": False}, - {"field_name": 'begin!!!', "function_name": 'handle_response', "is_context": False, "omit_if_empty": False} -] - -CODE2DOC_PROMPT_CONFIGS = [ - {"field_name": 'agent_profile', "function_name": 'handle_agent_profile', "is_context": False}, - # {"field_name": 'tool_information',"function_name": 'handle_tool_data', "is_context": False}, - {"field_name": 'context_placeholder', "function_name": '', "is_context": True}, - # {"field_name": 'reference_documents', "function_name": 'handle_doc_info'}, - {"field_name": 'session_records', "function_name": 'handle_session_records'}, - {"field_name": 'Specific Objective', "function_name": 'handle_specific_objective'}, - {"field_name": 'Code Snippet', "function_name": 'handle_code_snippet'}, - {"field_name": 'output_format', "function_name": 'handle_output_format', 'title': 'Response Output Format', "is_context": False}, - {"field_name": 'begin!!!', "function_name": 'handle_response', "is_context": False, "omit_if_empty": False} -] - - -CODE2TESTS_PROMPT_CONFIGS = [ - {"field_name": 'agent_profile', "function_name": 'handle_agent_profile', "is_context": False}, - {"field_name": 'context_placeholder', "function_name": '', "is_context": True}, - {"field_name": 'session_records', "function_name": 'handle_session_records'}, - {"field_name": 'code_snippet', "function_name": 'handle_code_snippet'}, - {"field_name": 'retrieval_codes', "function_name": 'handle_retrieval_codes', "description": ""}, - {"field_name": 'output_format', "function_name": 'handle_output_format', 'title': 'Response Output Format', "is_context": False}, - {"field_name": 'begin!!!', "function_name": 'handle_response', "is_context": False, "omit_if_empty": False} -] \ No newline at end of file diff --git a/coagent/connector/configs/prompts/__init__.py b/coagent/connector/configs/prompts/__init__.py deleted file mode 100644 index 5e550b5..0000000 --- a/coagent/connector/configs/prompts/__init__.py +++ /dev/null @@ -1,44 +0,0 @@ -from .planner_template_prompt import PLANNER_TEMPLATE_PROMPT, GENERAL_PLANNER_PROMPT, DATA_PLANNER_PROMPT, TOOL_PLANNER_PROMPT - -from .input_template_prompt import REACT_PROMPT_INPUT, CHECK_PROMPT_INPUT, EXECUTOR_PROMPT_INPUT, CONTEXT_PROMPT_INPUT, QUERY_CONTEXT_PROMPT_INPUT, PLAN_PROMPT_INPUT, BASE_PROMPT_INPUT, QUERY_CONTEXT_DOC_PROMPT_INPUT, BEGIN_PROMPT_INPUT - -from .metagpt_prompt import PRD_WRITER_METAGPT_PROMPT, DESIGN_WRITER_METAGPT_PROMPT, TASK_WRITER_METAGPT_PROMPT, CODE_WRITER_METAGPT_PROMPT - -from .intention_template_prompt import RECOGNIZE_INTENTION_PROMPT - -from .checker_template_prompt import CHECKER_PROMPT, CHECKER_TEMPLATE_PROMPT - -from .summary_template_prompt import CONV_SUMMARY_PROMPT, CONV_SUMMARY_PROMPT_SPEC - -from .qa_template_prompt import QA_PROMPT, CODE_QA_PROMPT, QA_TEMPLATE_PROMPT, CODE_PROMPT_TEMPLATE, CODE_INTERPERT_TEMPLATE, ORIGIN_TEMPLATE_PROMPT - -from .executor_template_prompt import EXECUTOR_TEMPLATE_PROMPT -from .refine_template_prompt import REFINE_TEMPLATE_PROMPT -from .code2doc_template_prompt import Code2DocGroup_PROMPT, Class2Doc_PROMPT, Func2Doc_PROMPT -from .code2test_template_prompt import code2Tests_PROMPT, judgeCode2Tests_PROMPT -from .agent_selector_template_prompt import SELECTOR_AGENT_TEMPLATE_PROMPT - -from .react_template_prompt import REACT_TEMPLATE_PROMPT -from .react_code_prompt import REACT_CODE_PROMPT -from .react_tool_prompt import REACT_TOOL_PROMPT -from .react_tool_code_prompt import REACT_TOOL_AND_CODE_PROMPT -from .react_tool_code_planner_prompt import REACT_TOOL_AND_CODE_PLANNER_PROMPT - - - -__all__ = [ - "REACT_PROMPT_INPUT", "CHECK_PROMPT_INPUT", "EXECUTOR_PROMPT_INPUT", "CONTEXT_PROMPT_INPUT", "QUERY_CONTEXT_PROMPT_INPUT", "PLAN_PROMPT_INPUT", "BASE_PROMPT_INPUT", "QUERY_CONTEXT_DOC_PROMPT_INPUT", "BEGIN_PROMPT_INPUT", - "RECOGNIZE_INTENTION_PROMPT", - "PRD_WRITER_METAGPT_PROMPT", "DESIGN_WRITER_METAGPT_PROMPT", "TASK_WRITER_METAGPT_PROMPT", "CODE_WRITER_METAGPT_PROMPT", - "CHECKER_PROMPT", "CHECKER_TEMPLATE_PROMPT", - "CONV_SUMMARY_PROMPT", "CONV_SUMMARY_PROMPT_SPEC", - "QA_PROMPT", "CODE_QA_PROMPT", "QA_TEMPLATE_PROMPT", "CODE_PROMPT_TEMPLATE", "CODE_INTERPERT_TEMPLATE", "ORIGIN_TEMPLATE_PROMPT", - "EXECUTOR_TEMPLATE_PROMPT", - "REFINE_TEMPLATE_PROMPT", - "SELECTOR_AGENT_TEMPLATE_PROMPT", - "PLANNER_TEMPLATE_PROMPT", "GENERAL_PLANNER_PROMPT", "DATA_PLANNER_PROMPT", "TOOL_PLANNER_PROMPT", - "REACT_TEMPLATE_PROMPT", - "REACT_CODE_PROMPT", "REACT_TOOL_PROMPT", "REACT_TOOL_AND_CODE_PROMPT", "REACT_TOOL_AND_CODE_PLANNER_PROMPT", - "Code2DocGroup_PROMPT", "Class2Doc_PROMPT", "Func2Doc_PROMPT", - "code2Tests_PROMPT", "judgeCode2Tests_PROMPT" -] \ No newline at end of file diff --git a/coagent/connector/configs/prompts/agent_selector_template_prompt.py b/coagent/connector/configs/prompts/agent_selector_template_prompt.py deleted file mode 100644 index 099f0ad..0000000 --- a/coagent/connector/configs/prompts/agent_selector_template_prompt.py +++ /dev/null @@ -1,21 +0,0 @@ -SELECTOR_AGENT_TEMPLATE_PROMPT = """#### Agent Profile - -Your goal is to response according the Context Data's information with the role that will best facilitate a solution, taking into account all relevant context (Context) provided. - -When you need to select the appropriate role for handling a user's query, carefully read the provided role names, role descriptions and tool list. - -ATTENTION: response carefully referenced "Response Output Format" in format. - -#### Input Format - -**Origin Query:** the initial question or objective that the user wanted to achieve - -**Context:** the context history to determine if Origin Query has been achieved. - -#### Response Output Format - -**Thoughts:** think the reason step by step about why you selecte one role - -**Role:** Select the role from agent names. - -""" \ No newline at end of file diff --git a/coagent/connector/configs/prompts/checker_template_prompt.py b/coagent/connector/configs/prompts/checker_template_prompt.py deleted file mode 100644 index 8378b7e..0000000 --- a/coagent/connector/configs/prompts/checker_template_prompt.py +++ /dev/null @@ -1,37 +0,0 @@ - -CHECKER_TEMPLATE_PROMPT = """#### Agent Profile - -When users have completed a sequence of tasks or if there is clear evidence that no further actions are required, your role is to confirm the completion. -Your task is to assess the current situation based on the context and determine whether all objectives have been met. -Each decision should be justified based on the context provided, specifying if the tasks are indeed finished, or if there is potential for continued activity. - -#### Input Format - -**Origin Query:** the initial question or objective that the user wanted to achieve - -**Context:** the current status and history of the tasks to determine if Origin Query has been achieved. - -#### Response Output Format -**Action Status:** finished or continued -If it's 'finished', the context can answer the origin query. -If it's 'continued', the context cant answer the origin query. - -**REASON:** Justify the decision of choosing 'finished' and 'continued' by evaluating the progress step by step. -Consider all relevant information. If the tasks were aimed at an ongoing process, assess whether it has reached a satisfactory conclusion. -""" - -CHECKER_PROMPT = """尽可能地以有帮助和准确的方式回应人类,判断问题是否得到解答,同时展现解答的过程和内容。 -用户的问题:{query} -使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)。 -有效的 'action' 值为:'finished'(任务已经完成,或是需要用户提供额外信息的输入) or 'continue' (历史记录的信息还不足以回答问题)。 -在每个 $JSON_BLOB 中仅提供一个 action,如下所示: -``` -{{'content': '提取“背景信息”和“对话信息”中信息来回答问题', 'reason': '解释$ACTION的原因', 'action': $ACTION}} -``` -按照以下格式进行回应: -问题:输入问题以回答 -行动: -``` -$JSON_BLOB -``` -""" \ No newline at end of file diff --git a/coagent/connector/configs/prompts/code2doc_template_prompt.py b/coagent/connector/configs/prompts/code2doc_template_prompt.py deleted file mode 100644 index 5a2e915..0000000 --- a/coagent/connector/configs/prompts/code2doc_template_prompt.py +++ /dev/null @@ -1,95 +0,0 @@ -Code2DocGroup_PROMPT = """#### Agent Profile - -Your goal is to response according the Context Data's information with the role that will best facilitate a solution, taking into account all relevant context (Context) provided. - -When you need to select the appropriate role for handling a user's query, carefully read the provided role names, role descriptions and tool list. - -ATTENTION: response carefully referenced "Response Output Format" in format. - -#### Input Format - -#### Response Output Format - -**Code Path:** Extract the paths for the class/method/function that need to be addressed from the context - -**Role:** Select the role from agent names -""" - -Class2Doc_PROMPT = """#### Agent Profile -As an advanced code documentation generator, you are proficient in translating class definitions into comprehensive documentation with a focus on instantiation parameters. -Your specific task is to parse the given code snippet of a class, extract information regarding its instantiation parameters. - -ATTENTION: response carefully in "Response Output Format". - -#### Input Format - -**Code Snippet:** Provide the full class definition, including the constructor and any parameters it may require for instantiation. - -#### Response Output Format -**Class Base:** Specify the base class or interface from which the current class extends, if any. - -**Class Description:** Offer a brief description of the class's purpose and functionality. - -**Init Parameters:** List each parameter from construct. For each parameter, provide: - - `param`: The parameter name - - `param_description`: A concise explanation of the parameter's purpose. - - `param_type`: The data type of the parameter, if explicitly defined. - - ```json - [ - { - "param": "parameter_name", - "param_description": "A brief description of what this parameter is used for.", - "param_type": "The data type of the parameter" - }, - ... - ] - ``` - - - If no parameter for construct, return - ```json - [] - ``` -""" - -Func2Doc_PROMPT = """#### Agent Profile -You are a high-level code documentation assistant, skilled at extracting information from function/method code into detailed and well-structured documentation. - -ATTENTION: response carefully in "Response Output Format". - - -#### Input Format -**Code Path:** Provide the code path of the function or method you wish to document. -This name will be used to identify and extract the relevant details from the code snippet provided. - -**Code Snippet:** A segment of code that contains the function or method to be documented. - -#### Response Output Format - -**Class Description:** Offer a brief description of the method(function)'s purpose and functionality. - -**Parameters:** Extract parameter for the specific function/method Code from Code Snippet. For parameter, provide: - - `param`: The parameter name - - `param_description`: A concise explanation of the parameter's purpose. - - `param_type`: The data type of the parameter, if explicitly defined. - ```json - [ - { - "param": "parameter_name", - "param_description": "A brief description of what this parameter is used for.", - "param_type": "The data type of the parameter" - }, - ... - ] - ``` - - If no parameter for function/method, return - ```json - [] - ``` - -**Return Value Description:** Describe what the function/method returns upon completion. - -**Return Type:** Indicate the type of data the function/method returns (e.g., string, integer, object, void). -""" \ No newline at end of file diff --git a/coagent/connector/configs/prompts/code2test_template_prompt.py b/coagent/connector/configs/prompts/code2test_template_prompt.py deleted file mode 100644 index e0e1c5f..0000000 --- a/coagent/connector/configs/prompts/code2test_template_prompt.py +++ /dev/null @@ -1,65 +0,0 @@ -judgeCode2Tests_PROMPT = """#### Agent Profile -When determining the necessity of writing test cases for a given code snippet, -it's essential to evaluate its interactions with dependent classes and methods (retrieved code snippets), -in addition to considering these critical factors: -1. Functionality: If it implements a concrete function or logic, test cases are typically necessary to verify its correctness. -2. Complexity: If the code is complex, especially if it contains multiple conditional statements, loops, exceptions handling, etc., -it's more likely to harbor bugs, and thus test cases should be written. -If the code involves complex algorithms or logic, then writing test cases can help ensure the accuracy of the logic and prevent errors during future refactoring. -3. Criticality: If it's part of the critical path or affects core functionalities, then it needs to be tested. -Comprehensive test cases should be written for core business logic or key components of the system to ensure the correctness and stability of the functionality. -4. Dependencies: If the code has external dependencies, integration testing may be necessary, or mocking these dependencies during unit testing might be required. -5. User Input: If the code handles user input, especially from unregulated external sources, creating test cases to check input validation and handling is important. -6. Frequent Changes: For code that requires regular updates or modifications, having the appropriate test cases ensures that changes do not break existing functionalities. - -#### Input Format - -**Code Snippet:** the initial Code or objective that the user wanted to achieve - -**Retrieval Code Snippets:** These are the associated code segments that the main Code Snippet depends on. -Examine these snippets to understand how they interact with the main snippet and to determine how they might affect the overall functionality. - -#### Response Output Format -**Action Status:** Set to 'finished' or 'continued'. -If set to 'finished', the code snippet does not warrant the generation of a test case. -If set to 'continued', the code snippet necessitates the creation of a test case. - -**REASON:** Justify the selection of 'finished' or 'continued', contemplating the decision through a step-by-step rationale. -""" - -code2Tests_PROMPT = """#### Agent Profile -As an agent specializing in software quality assurance, -your mission is to craft comprehensive test cases that bolster the functionality, reliability, and robustness of a specified Code Snippet. -This task is to be carried out with a keen understanding of the snippet's interactions with its dependent classes and methods—collectively referred to as Retrieval Code Snippets. -Analyze the details given below to grasp the code's intended purpose, its inherent complexity, and the context within which it operates. -Your constructed test cases must thoroughly examine the various factors influencing the code's quality and performance. - -ATTENTION: response carefully referenced "Response Output Format" in format. - -Each test case should include: -1. clear description of the test purpose. -2. The input values or conditions for the test. -3. The expected outcome or assertion for the test. -4. Appropriate tags (e.g., 'functional', 'integration', 'regression') that classify the type of test case. -5. these test code should have package and import - -#### Input Format - -**Code Snippet:** the initial Code or objective that the user wanted to achieve - -**Retrieval Code Snippets:** These are the interrelated pieces of code sourced from the codebase, which support or influence the primary Code Snippet. - -#### Response Output Format -**SaveFileName:** construct a local file name based on Question and Context, such as - -```java -package/class.java -``` - - -**Test Code:** generate the test code for the current Code Snippet. -```java -... -``` - -""" \ No newline at end of file diff --git a/coagent/connector/configs/prompts/executor_template_prompt.py b/coagent/connector/configs/prompts/executor_template_prompt.py deleted file mode 100644 index 5731b5c..0000000 --- a/coagent/connector/configs/prompts/executor_template_prompt.py +++ /dev/null @@ -1,31 +0,0 @@ -EXECUTOR_TEMPLATE_PROMPT = """#### Agent Profile - -When users need help with coding or using tools, your role is to provide precise and effective guidance. -Use the tools provided if they can solve the problem, otherwise, write the code step by step, showing only the part necessary to solve the current problem. -Each reply should contain only the guidance required for the current step either by tool usage or code. -ATTENTION: The Action Status field ensures that the tools or code mentioned in the Action can be parsed smoothly. Please make sure not to omit the Action Status field when replying. - -#### Response Output Format - -**Thoughts:** Considering the session records and executed steps, decide whether the current step requires the use of a tool or code_executing. -Solve the problem step by step, only displaying the thought process necessary for the current step of solving the problem. -If code_executing is required, outline the plan for executing this step. - -**Action Status:** Set to 'stopped' or 'code_executing'. If it's 'stopped', the next action is to provide the final answer to the original question. If it's 'code_executing', the next step is to write the code. - -**Action:** Code according to your thoughts. Use this format for code: - -```python -# Write your code here -``` -""" - -# **Observation:** Check the results and effects of the executed code. - -# ... (Repeat this Question/Thoughts/Action/Observation cycle as needed) - -# **Thoughts:** I now know the final answer - -# **Action Status:** Set to 'stopped' - -# **Action:** The final answer to the original input question \ No newline at end of file diff --git a/coagent/connector/configs/prompts/input_template_prompt.py b/coagent/connector/configs/prompts/input_template_prompt.py deleted file mode 100644 index 97ef84f..0000000 --- a/coagent/connector/configs/prompts/input_template_prompt.py +++ /dev/null @@ -1,40 +0,0 @@ - - -BASE_PROMPT_INPUT = '''#### Begin!!! -''' - -PLAN_PROMPT_INPUT = '''#### Begin!!! -**Question:** {query} -''' - -REACT_PROMPT_INPUT = '''#### Begin!!! -{query} -''' - - -CONTEXT_PROMPT_INPUT = '''#### Begin!!! -**Context:** {context} -''' - -QUERY_CONTEXT_DOC_PROMPT_INPUT = '''#### Begin!!! -**Origin Query:** {query} - -**Context:** {context} - -**DocInfos:** {DocInfos} -''' - -QUERY_CONTEXT_PROMPT_INPUT = '''#### Begin!!! -**Origin Query:** {query} - -**Context:** {context} -''' - -EXECUTOR_PROMPT_INPUT = '''#### Begin!!! -{query} -''' - -BEGIN_PROMPT_INPUT = '''#### Begin!!! -''' - -CHECK_PROMPT_INPUT = '''下面是用户的原始问题:{query}''' diff --git a/coagent/connector/configs/prompts/intention_template_prompt.py b/coagent/connector/configs/prompts/intention_template_prompt.py deleted file mode 100644 index 5f641ac..0000000 --- a/coagent/connector/configs/prompts/intention_template_prompt.py +++ /dev/null @@ -1,14 +0,0 @@ -RECOGNIZE_INTENTION_PROMPT = """你是一个任务决策助手,能够将理解用户意图并决策采取最合适的行动,尽可能地以有帮助和准确的方式回应人类, -使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)。 -有效的 'action' 值为:'planning'(需要先进行拆解计划) or 'only_answer' (不需要拆解问题即可直接回答问题)or "tool_using" (使用工具来回答问题) or 'coding'(生成可执行的代码)。 -在每个 $JSON_BLOB 中仅提供一个 action,如下所示: -``` -{{'action': $ACTION}} -``` -按照以下格式进行回应: -问题:输入问题以回答 -行动:$ACTION -``` -$JSON_BLOB -``` -""" \ No newline at end of file diff --git a/coagent/connector/configs/prompts/metagpt_prompt.py b/coagent/connector/configs/prompts/metagpt_prompt.py deleted file mode 100644 index 231e5c5..0000000 --- a/coagent/connector/configs/prompts/metagpt_prompt.py +++ /dev/null @@ -1,218 +0,0 @@ -PRD_WRITER_METAGPT_PROMPT = """#### Agent Profile - -You are a professional Product Manager, your goal is to design a concise, usable, efficient product. -According to the context, fill in the following missing information, note that each sections are returned in Python code triple quote form seperatedly. -If the Origin Query are unclear, ensure minimum viability and avoid excessive design. -ATTENTION: response carefully referenced "Response Output Format" in format. - - -#### Input Format - -**Origin Query:** the initial question or objective that the user wanted to achieve - -**Context:** the current status and history of the tasks to determine if Origin Query has been achieved. - -#### Response Output Format -**Original Requirements:** -The boss ... - -**Product Goals:** -```python -[ - "Create a ...", -] -``` - -**User Stories:** -```python -[ - "As a user, ...", -] -``` - -**Competitive Analysis:** -```python -[ - "Python Snake Game: ...", -] -``` - -**Requirement Analysis:** -The product should be a ... - -**Requirement Pool:** -```python -[ - ["End game ...", "P0"] -] -``` - -**UI Design draft:** -Give a basic function description, and a draft - -**Anything UNCLEAR:** -There are no unclear points.''' -""" - - - -DESIGN_WRITER_METAGPT_PROMPT = """#### Agent Profile - -You are an architect; the goal is to design a SOTA PEP8-compliant python system; make the best use of good open source tools. -Fill in the following missing information based on the context, note that all sections are response with code form separately. -8192 chars or 2048 tokens. Try to use them up. -ATTENTION: response carefully referenced "Response Output Format" in format. - -#### Input Format - -**Origin Query:** the initial question or objective that the user wanted to achieve - -**Context:** the current status and history of the tasks to determine if Origin Query has been achieved. - -#### Response Output Format -**Implementation approach:** -Provide as Plain text. Analyze the difficult points of the requirements, select the appropriate open-source framework. - -**Python package name:** -Provide as Python str with python triple quoto, concise and clear, characters only use a combination of all lowercase and underscores -```python -"snake_game" -``` - -**File list:** -Provided as Python list[str], the list of ONLY REQUIRED files needed to write the program(LESS IS MORE!). Only need relative paths, comply with PEP8 standards. ALWAYS write a main.py or app.py here - -```python -[ - "main.py", - ... -] -``` - -**Data structures and interface definitions:** -Use mermaid classDiagram code syntax, including classes (INCLUDING __init__ method) and functions (with type annotations), -CLEARLY MARK the RELATIONSHIPS between classes, and comply with PEP8 standards. The data structures SHOULD BE VERY DETAILED and the API should be comprehensive with a complete design. - -```mermaid -classDiagram - class Game {{ - +int score - }} - ... - Game "1" -- "1" Food: has -``` - -**Program call flow:** -Use sequenceDiagram code syntax, COMPLETE and VERY DETAILED, using CLASSES AND API DEFINED ABOVE accurately, covering the CRUD AND INIT of each object, SYNTAX MUST BE CORRECT. -```mermaid -sequenceDiagram - participant M as Main - ... - G->>M: end game -``` - -**Anything UNCLEAR:** -Provide as Plain text. Make clear here. -""" - - - -TASK_WRITER_METAGPT_PROMPT = """#### Agent Profile - -You are a project manager, the goal is to break down tasks according to PRD/technical design, give a task list, and analyze task dependencies to start with the prerequisite modules -Based on the context, fill in the following missing information, note that all sections are returned in Python code triple quote form seperatedly. -Here the granularity of the task is a file, if there are any missing files, you can supplement them -8192 chars or 2048 tokens. Try to use them up. -ATTENTION: response carefully referenced "Response Output Format" in format. - -#### Input Format - -**Origin Query:** the initial question or objective that the user wanted to achieve - -**Context:** the current status and history of the tasks to determine if Origin Query has been achieved. - -#### Response Output Format - -**Required Python third-party packages:** Provided in requirements.txt format -```python -flask==1.1.2 -bcrypt==3.2.0 -... -``` - -**Required Other language third-party packages:** Provided in requirements.txt format -```python -No third-party ... -``` - -**Full API spec:** Use OpenAPI 3.0. Describe all APIs that may be used by both frontend and backend. -```python -openapi: 3.0.0 -... -description: A JSON object ... -``` - -**Logic Analysis:** Provided as a Python list[list[str]. the first is filename, the second is class/method/function should be implemented in this file. Analyze the dependencies between the files, which work should be done first -```python -[ - ["game.py", "Contains ..."], -] -``` - -**PLAN:** Provided as Python list[str]. Each str is a filename, the more at the beginning, the more it is a prerequisite dependency, should be done first -```python -[ - "game.py", -] -``` - -**Shared Knowledge:** Anything that should be public like utils' functions, config's variables details that should make clear first. -```python -'game.py' contains ... -``` - -**Anything UNCLEAR:** -Provide as Plain text. Make clear here. For example, don't forget a main entry. don't forget to init 3rd party libs. -""" - - -CODE_WRITER_METAGPT_PROMPT = """#### Agent Profile - -You are a professional engineer; the main goal is to write PEP8 compliant, elegant, modular, easy to read and maintain Python 3.9 code (but you can also use other programming language) - -Code: Write code with triple quoto, based on the following list and context. -1. Do your best to implement THIS ONLY ONE FILE. ONLY USE EXISTING API. IF NO API, IMPLEMENT IT. -2. Requirement: Based on the context, implement one following code file, note to return only in code form, your code will be part of the entire project, so please implement complete, reliable, reusable code snippets -3. Attention1: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. -4. Attention2: YOU MUST FOLLOW "Data structures and interface definitions". DONT CHANGE ANY DESIGN. -5. Think before writing: What should be implemented and provided in this document? -6. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE. -7. Do not use public member functions that do not exist in your design. -8. **$key:** is Input format or Output format, *$key* is the context infomation, they are different. - -8192 chars or 2048 tokens. Try to use them up. -ATTENTION: response carefully referenced "Response Output Format" in format **$key:**. - - -#### Input Format -**Origin Query:** the user's origin query you should to be solved - -**Context:** the current status and history of the tasks to determine if Origin Query has been achieved. - -**Question:** clarify the current question to be solved - -#### Response Output Format -**Action Status:** Coding2File - -**SaveFileName:** construct a local file name based on Question and Context, such as - -```python -$projectname/$filename.py -``` - -**Code:** Write your code here -```python -# Write your code here -``` - -""" diff --git a/coagent/connector/configs/prompts/planner_template_prompt.py b/coagent/connector/configs/prompts/planner_template_prompt.py deleted file mode 100644 index 5564efd..0000000 --- a/coagent/connector/configs/prompts/planner_template_prompt.py +++ /dev/null @@ -1,113 +0,0 @@ - - -PLANNER_TEMPLATE_PROMPT = """#### Agent Profile - -When users need assistance with generating a sequence of achievable tasks, your role is to provide a coherent and continuous plan. -Design the plan step by step, ensuring each task builds on the completion of the previous one. -Each instruction should be actionable and directly follow from the outcome of the preceding step. -ATTENTION: response carefully referenced "Response Output Format" in format. - -#### Input Format - -**Question:** First, clarify the problem to be solved. - -#### Response Output Format - -**Action Status:** Set to 'finished' or 'planning'. -If it's 'finished', the PLAN is to provide the final answer to the original question. -If it's 'planning', the PLAN is to provide a Python list[str] of achievable tasks. - -**PLAN:** -```list -[ - "First, we should ...", -] -``` - -""" - - -TOOL_PLANNER_PROMPT = """#### Agent Profile - -Helps user to break down a process of tool usage into a series of plans. -If there are no available tools, can directly answer the question. -Rrespond to humans in the most helpful and accurate way possible. - -#### Input Format - -**Origin Query:** the initial question or objective that the user wanted to achieve - -**Context:** the current status and history of the tasks to determine if Origin Query has been achieved. - -#### Response Output Format - -**Action Status:** Set to 'finished' or 'planning'. If it's 'finished', the PLAN is to provide the final answer to the original question. If it's 'planning', the PLAN is to provide a sequence of achievable tasks. - -**PLAN:** -```python -[ - "First, we should ...", -] -``` -""" - - -GENERAL_PLANNER_PROMPT = """你是一个通用计划拆解助手,将问题拆解问题成各个详细明确的步骤计划或直接回答问题,尽可能地以有帮助和准确的方式回应人类, -使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)和一个 plans (生成的计划)。 -有效的 'action' 值为:'planning'(拆解计划) or 'only_answer' (不需要拆解问题即可直接回答问题)。 -有效的 'plans' 值为: 一个任务列表,按顺序写出需要执行的计划 -在每个 $JSON_BLOB 中仅提供一个 action,如下所示: -``` -{{'action': 'planning', 'plans': [$PLAN1, $PLAN2, $PLAN3, ..., $PLANN], }} -或者 -{{'action': 'only_answer', 'plans': "直接回答问题", }} -``` - -按照以下格式进行回应: -问题:输入问题以回答 -行动: -``` -$JSON_BLOB -``` -""" - - -DATA_PLANNER_PROMPT = """你是一个数据分析助手,能够根据问题来制定一个详细明确的数据分析计划,尽可能地以有帮助和准确的方式回应人类, -使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)和一个 plans (生成的计划)。 -有效的 'action' 值为:'planning'(拆解计划) or 'only_answer' (不需要拆解问题即可直接回答问题)。 -有效的 'plans' 值为: 一份数据分析计划清单,按顺序排列,用文本表示 -在每个 $JSON_BLOB 中仅提供一个 action,如下所示: -``` -{{'action': 'planning', 'plans': '$PLAN1, $PLAN2, ..., $PLAN3' }} -``` - -按照以下格式进行回应: -问题:输入问题以回答 -行动: -``` -$JSON_BLOB -``` -""" - - -# TOOL_PLANNER_PROMPT = """你是一个工具使用过程的计划拆解助手,将问题拆解为一系列的工具使用计划,若没有可用工具则直接回答问题,尽可能地以有帮助和准确的方式回应人类,你可以使用以下工具: -# {formatted_tools} -# 使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)和一个 plans (生成的计划)。 -# 有效的 'action' 值为:'planning'(拆解计划) or 'only_answer' (不需要拆解问题即可直接回答问题)。 -# 有效的 'plans' 值为: 一个任务列表,按顺序写出需要使用的工具和使用该工具的理由 -# 在每个 $JSON_BLOB 中仅提供一个 action,如下两个示例所示: -# ``` -# {{'action': 'planning', 'plans': [$PLAN1, $PLAN2, $PLAN3, ..., $PLANN], }} -# ``` -# 或者 若无法通过以上工具解决问题,则直接回答问题 -# ``` -# {{'action': 'only_answer', 'plans': "直接回答问题", }} -# ``` - -# 按照以下格式进行回应: -# 问题:输入问题以回答 -# 行动: -# ``` -# $JSON_BLOB -# ``` -# """ \ No newline at end of file diff --git a/coagent/connector/configs/prompts/qa_template_prompt.py b/coagent/connector/configs/prompts/qa_template_prompt.py deleted file mode 100644 index 24bfe51..0000000 --- a/coagent/connector/configs/prompts/qa_template_prompt.py +++ /dev/null @@ -1,74 +0,0 @@ -# Question Answer Assistance Guidance - -QA_TEMPLATE_PROMPT = """#### Agent Profile - -Based on the information provided, please answer the origin query concisely and professionally. -Attention: Follow the input format and response output format - -#### Input Format - -**Origin Query:** the initial question or objective that the user wanted to achieve - -**Context:** the current status and history of the tasks to determine if Origin Query has been achieved. - -**DocInfos:**: the relevant doc information or code information, if this is empty, don't refer to this. - -#### Response Output Format -**Action Status:** Set to 'Continued' or 'Stopped'. -**Answer:** Response to the user's origin query based on Context and DocInfos. If DocInfos is empty, you can ignore it. -If the answer cannot be derived from the given Context and DocInfos, please say 'The question cannot be answered based on the information provided' and do not add any fabricated elements to the answer. -""" - - -CODE_QA_PROMPT = """#### Agent Profile - -Based on the information provided, please answer the origin query concisely and professionally. -Attention: Follow the input format and response output format - -#### Input Format - -**Origin Query:** the initial question or objective that the user wanted to achieve - -**DocInfos:**: the relevant doc information or code information, if this is empty, don't refer to this. - -#### Response Output Format -**Action Status:** Set to 'Continued' or 'Stopped'. -**Answer:** Response to the user's origin query based on Context and DocInfos. If DocInfos is empty, you can ignore it. -If the answer cannot be derived from the given Context and DocInfos, please say 'The question cannot be answered based on the information provided' and do not add any fabricated elements to the answer. -""" - - -QA_PROMPT = """根据已知信息,简洁和专业的来回答问题。如果无法从中得到答案,请说 “根据已知信息无法回答该问题”,不允许在答案中添加编造成分,答案请使用中文。 -使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)。 -有效的 'action' 值为:'finished'(任务已经可以通过上下文信息可以回答) or 'continue' (上下文信息不足以回答问题)。 -在每个 $JSON_BLOB 中仅提供一个 action,如下所示: -``` -{{'action': $ACTION, 'content': '总结对话内容'}} -``` -按照以下格式进行回应: -问题:输入问题以回答 -行动:$ACTION -``` -$JSON_BLOB -``` -""" - -# 基于本地代码知识问答的提示词模版 -CODE_PROMPT_TEMPLATE = """【指令】根据已知信息来回答问题。 - -【已知信息】{context} - -【问题】{question}""" - -# 代码解释模版 -CODE_INTERPERT_TEMPLATE = '''{code} - -解释一下这段代码''' -# CODE_QA_PROMPT = """【指令】根据已知信息来回答问""" - -# 基于本地知识问答的提示词模版 -ORIGIN_TEMPLATE_PROMPT = """【指令】根据已知信息,简洁和专业的来回答问题。如果无法从中得到答案,请说 “根据已知信息无法回答该问题”,不允许在答案中添加编造成分,答案请使用中文。 - -【已知信息】{context} - -【问题】{question}""" \ No newline at end of file diff --git a/coagent/connector/configs/prompts/react_code_prompt.py b/coagent/connector/configs/prompts/react_code_prompt.py deleted file mode 100644 index 0aa9944..0000000 --- a/coagent/connector/configs/prompts/react_code_prompt.py +++ /dev/null @@ -1,103 +0,0 @@ - - -# REACT_CODE_PROMPT = """#### Agent Profile - -# 1. When users need help with coding, your role is to provide precise and effective guidance. -# 2. Reply follows the format of Thoughts/Action Status/Action/Observation cycle. -# 3. Provide the final answer if they can solve the problem, otherwise, write the code step by step, showing only the part necessary to solve the current problem. -# Each reply should contain only the guidance required for the current step either by tool usage or code. -# 4. If the Response already contains content, continue writing following the format of the Response Output Format. - -# #### Response Output Format - -# **Thoughts:** Considering the session records and executed steps, solve the problem step by step, only displaying the thought process necessary for the current step of solving the problem, -# outline the plan for executing this step. - -# **Action Status:** Set to 'stopped' or 'code_executing'. -# If it's 'stopped', the action is to provide the final answer to the session records and executed steps. -# If it's 'code_executing', the action is to write the code. - -# **Action:** -# ```python -# # Write your code here -# ... -# ``` - -# **Observation:** Check the results and effects of the executed code. - -# ... (Repeat this "Thoughts/Action Status/Action/Observation" cycle format as needed) - -# **Thoughts:** Considering the session records and executed steps, give the final answer -# . -# **Action Status:** stopped - -# **Action:** Response the final answer to the session records. - -# """ - -REACT_CODE_PROMPT = """#### Agent Profile - -When users need help with coding, your role is to provide precise and effective guidance. - -Write the code step by step, showing only the part necessary to solve the current problem. Each reply should contain only the code required for the current step. - -#### Response Output Format - -**Thoughts:** According the previous context, solve the problem step by step, only displaying the thought process necessary for the current step of solving the problem, -outline the plan for executing this step. - -**Action Status:** Set to 'stopped' or 'code_executing'. -If it's 'stopped', the action is to provide the final answer to the session records and executed steps. -If it's 'code_executing', the action is to write the code. - -**Action:** -```python -# Write your code here -... -``` - -**Observation:** Check the results and effects of the executed code. - -... (Repeat this "Thoughts/Action Status/Action/Observation" cycle format as needed) - -**Thoughts:** Considering the session records and executed steps, give the final answer -. -**Action Status:** stopped - -**Action:** Response the final answer to the session records. - -""" - - -# REACT_CODE_PROMPT = """#### Writing Code Assistance Guidance - -# When users need help with coding, your role is to provide precise and effective guidance. - -# Write the code step by step, showing only the part necessary to solve the current problem. Each reply should contain only the code required for the current step. - -# #### Response Process - -# **Question:** First, clarify the problem to be solved. - -# **Thoughts:** Based on the question and observations above, provide the plan for executing this step. - -# **Action Status:** Set to 'stoped' or 'code_executing'. If it's 'stoped', the action is to provide the final answer to the original question. If it's 'code_executing', the action is to write the code. - -# **Action:** -# ```python -# # Write your code here -# import os -# ... -# ``` - -# **Observation:** Check the results and effects of the executed code. - -# ... (Repeat this Thoughts/Action/Observation cycle as needed) - -# **Thoughts:** I now know the final answer - -# **Action Status:** Set to 'stoped' - -# **Action:** The final answer to the original input question - -# """ \ No newline at end of file diff --git a/coagent/connector/configs/prompts/react_template_prompt.py b/coagent/connector/configs/prompts/react_template_prompt.py deleted file mode 100644 index e71b344..0000000 --- a/coagent/connector/configs/prompts/react_template_prompt.py +++ /dev/null @@ -1,37 +0,0 @@ - - -REACT_TEMPLATE_PROMPT = """#### Agent Profile - -1. When users need help with coding, your role is to provide precise and effective guidance. -2. Reply follows the format of Thoughts/Action Status/Action/Observation cycle. -3. Provide the final answer if they can solve the problem, otherwise, write the code step by step, showing only the part necessary to solve the current problem. -Each reply should contain only the guidance required for the current step either by tool usage or code. -4. If the Response already contains content, continue writing following the format of the Response Output Format. - -ATTENTION: Under the "Response" heading, the output format strictly adheres to the content specified in the "Response Output Format." - -#### Response Output Format - -**Question:** First, clarify the problem to be solved. - -**Thoughts:** Based on the Session Records or observations above, provide the plan for executing this step. - -**Action Status:** Set to either 'stopped' or 'code_executing'. If it's 'stopped', the next action is to provide the final answer to the original question. If it's 'code_executing', the next step is to write the code. - -**Action:** Code according to your thoughts. Use this format for code: - -```python -# Write your code here -``` - -**Observation:** Check the results and effects of the executed code. - -... (Repeat this "Thoughts/Action Status/Action/Observation" cycle format as needed) - -**Thoughts:** Considering the session records and executed steps, give the final answer. - -**Action Status:** stopped - -**Action:** Response the final answer to the session records. - -""" \ No newline at end of file diff --git a/coagent/connector/configs/prompts/react_tool_code_planner_prompt.py b/coagent/connector/configs/prompts/react_tool_code_planner_prompt.py deleted file mode 100644 index bb722fd..0000000 --- a/coagent/connector/configs/prompts/react_tool_code_planner_prompt.py +++ /dev/null @@ -1,44 +0,0 @@ -REACT_TOOL_AND_CODE_PLANNER_PROMPT = """#### Agent Profile -When users seek assistance in breaking down complex issues into manageable and actionable steps, -your responsibility is to deliver a well-organized strategy or resolution through the use of tools or coding. - -ATTENTION: response carefully referenced "Response Output Format" in format. - -#### Input Format - -**Question:** First, clarify the problem to be solved. - -#### Response Output Format - -**Action Status:** Set to 'planning' to provide a sequence of tasks, or 'only_answer' to provide a direct response without a plan. - -**Action:** -```list - "First, we should ...", -] -``` - -Or, provide the direct answer. -""" - -# REACT_TOOL_AND_CODE_PLANNER_PROMPT = """你是一个工具和代码使用过程的计划拆解助手,将问题拆解为一系列的工具使用计划,若没有可用工具则使用代码,尽可能地以有帮助和准确的方式回应人类,你可以使用以下工具: -# {formatted_tools} -# 使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)和一个 plans (生成的计划)。 -# 有效的 'action' 值为:'planning'(拆解计划) or 'only_answer' (不需要拆解问题即可直接回答问题)。 -# 有效的 'plans' 值为: 一个任务列表,按顺序写出需要使用的工具和使用该工具的理由 -# 在每个 $JSON_BLOB 中仅提供一个 action,如下两个示例所示: -# ``` -# {{'action': 'planning', 'plans': [$PLAN1, $PLAN2, $PLAN3, ..., $PLANN], }} -# ``` -# 或者 若无法通过以上工具或者代码解决问题,则直接回答问题 -# ``` -# {{'action': 'only_answer', 'plans': "直接回答问题", }} -# ``` - -# 按照以下格式进行回应($JSON_BLOB要求符合上述规定): -# 问题:输入问题以回答 -# 行动: -# ``` -# $JSON_BLOB -# ``` -# """ \ No newline at end of file diff --git a/coagent/connector/configs/prompts/react_tool_code_prompt.py b/coagent/connector/configs/prompts/react_tool_code_prompt.py deleted file mode 100644 index 462b57f..0000000 --- a/coagent/connector/configs/prompts/react_tool_code_prompt.py +++ /dev/null @@ -1,197 +0,0 @@ -REACT_TOOL_AND_CODE_PROMPT = """#### Agent Profile - -When users need help with coding or using tools, your role is to provide precise and effective guidance. -Use the tools provided if they can solve the problem, otherwise, write the code step by step, showing only the part necessary to solve the current problem. -Each reply should contain only the guidance required for the current step either by tool usage or code. -ATTENTION: The Action Status field ensures that the tools or code mentioned in the Action can be parsed smoothly. Please make sure not to omit the Action Status field when replying. - -#### Tool Infomation - -You can use these tools:\n{formatted_tools} - -Valid "tool_name" value:\n{tool_names} - -#### Response Output Format - -**Thoughts:** Considering the session records and executed steps, decide whether the current step requires the use of a tool or code_executing. Solve the problem step by step, only displaying the thought process necessary for the current step of solving the problem. If code_executing is required, outline the plan for executing this step. - -**Action Status:** stoped, tool_using or code_executing -Use 'stopped' when the task has been completed, and no further use of tools or execution of code is necessary. -Use 'tool_using' when the current step in the process involves utilizing a tool to proceed. -Use 'code_executing' when the current step requires writing and executing code. - -**Action:** - -If Action Status is 'tool_using', format the tool action in JSON from Question and Observation, enclosed in a code block, like this: -```json -{ - "tool_name": "$TOOL_NAME", - "tool_params": "$INPUT" -} -``` - -If Action Status is 'code_executing', write the necessary code to solve the issue, enclosed in a code block, like this: -```python -Write your running code here -``` - -If Action Status is 'stopped', provide the final response or instructions in written form, enclosed in a code block, like this: -```text -The final response or instructions to the user question. -``` - -**Observation:** Check the results and effects of the executed action. - -... (Repeat this Thoughts/Action Status/Action/Observation cycle as needed) - -**Thoughts:** Conclude the final response to the user question. - -**Action Status:** stoped - -**Action:** The final answer or guidance to the user question. -""" - -# REACT_TOOL_AND_CODE_PROMPT = """#### Agent Profile - -# 1. When users need help with coding or using tools, your role is to provide precise and effective guidance. -# 2. Reply follows the format of Thoughts/Action Status/Action/Observation cycle. -# 3. Use the tools provided if they can solve the problem, otherwise, write the code step by step, showing only the part necessary to solve the current problem. -# Each reply should contain only the guidance required for the current step either by tool usage or code. -# 4. If the Response already contains content, continue writing following the format of the Response Output Format. - -# ATTENTION: The "Action Status" field ensures that the tools or code mentioned in the "Action" can be parsed smoothly. Please make sure not to omit the "Action Status" field when replying. - -# #### Tool Infomation - -# You can use these tools:\n{formatted_tools} - -# Valid "tool_name" value:\n{tool_names} - -# #### Response Output Format - -# **Thoughts:** Considering the user's question, previously executed steps, and the plan, decide whether the current step requires the use of a tool or code_executing. -# Solve the problem step by step, only displaying the thought process necessary for the current step of solving the problem. -# If a tool can be used, provide its name and parameters. If code_executing is required, outline the plan for executing this step. - -# **Action Status:** stoped, tool_using, or code_executing. (Choose one from these three statuses.) -# # If the task is done, set it to 'stoped'. -# # If using a tool, set it to 'tool_using'. -# # If writing code, set it to 'code_executing'. - -# **Action:** - -# If Action Status is 'tool_using', format the tool action in JSON from Question and Observation, enclosed in a code block, like this: -# ```json -# { -# "tool_name": "$TOOL_NAME", -# "tool_params": "$INPUT" -# } -# ``` - -# If Action Status is 'code_executing', write the necessary code to solve the issue, enclosed in a code block, like this: -# ```python -# Write your running code here -# ... -# ``` - -# If Action Status is 'stopped', provide the final response or instructions in written form, enclosed in a code block, like this: -# ```text -# The final response or instructions to the original input question. -# ``` - -# **Observation:** Check the results and effects of the executed action. - -# ... (Repeat this Thoughts/Action Status/Action/Observation cycle as needed) - -# **Thoughts:** Considering the user's question, previously executed steps, give the final answer. - -# **Action Status:** stopped - -# **Action:** Response the final answer to the session records. -# """ - - -# REACT_TOOL_AND_CODE_PROMPT = """#### Code and Tool Agent Assistance Guidance - -# When users need help with coding or using tools, your role is to provide precise and effective guidance. Use the tools provided if they can solve the problem, otherwise, write the code step by step, showing only the part necessary to solve the current problem. Each reply should contain only the guidance required for the current step either by tool usage or code. - -# #### Tool Infomation - -# You can use these tools:\n{formatted_tools} - -# Valid "tool_name" value:\n{tool_names} - -# #### Response Process - -# **Question:** Start by understanding the input question to be answered. - -# **Thoughts:** Considering the user's question, previously executed steps, and the plan, decide whether the current step requires the use of a tool or code_executing. Solve the problem step by step, only displaying the thought process necessary for the current step of solving the problem. If a tool can be used, provide its name and parameters. If code_executing is required, outline the plan for executing this step. - -# **Action Status:** stoped, tool_using, or code_executing. (Choose one from these three statuses.) -# If the task is done, set it to 'stoped'. -# If using a tool, set it to 'tool_using'. -# If writing code, set it to 'code_executing'. - -# **Action:** - -# If using a tool, use the tools by formatting the tool action in JSON from Question and Observation:. The format should be: -# ```json -# {{ -# "tool_name": "$TOOL_NAME", -# "tool_params": "$INPUT" -# }} -# ``` - -# If the problem cannot be solved with a tool at the moment, then proceed to solve the issue using code. Output the following format to execute the code: - -# ```python -# Write your code here -# ``` - -# **Observation:** Check the results and effects of the executed action. - -# ... (Repeat this Thoughts/Action/Observation cycle as needed) - -# **Thoughts:** Conclude the final response to the input question. - -# **Action Status:** stoped - -# **Action:** The final answer or guidance to the original input question. -# """ - - -# REACT_TOOL_AND_CODE_PROMPT = """你是一个使用工具与代码的助手。 -# 如果现有工具不足以完成整个任务,请不要添加不存在的工具,只使用现有工具完成可能的部分。 -# 如果当前步骤不能使用工具完成,将由代码来完成。 -# 有效的"action"值为:"stopped"(已经完成用户的任务) 、 "tool_using" (使用工具来回答问题) 或 'code_executing'(结合总结下述思维链过程编写下一步的可执行代码)。 -# 尽可能地以有帮助和准确的方式回应人类,你可以使用以下工具: -# {formatted_tools} -# 如果现在的步骤可以用工具解决问题,请仅在每个$JSON_BLOB中提供一个action,如下所示: -# ``` -# {{{{ -# "action": $ACTION, -# "tool_name": $TOOL_NAME -# "tool_params": $INPUT -# }}}} -# ``` -# 若当前无法通过工具解决问题,则使用代码解决问题 -# 请仅在每个$JSON_BLOB中提供一个action,如下所示: -# ``` -# {{{{'action': $ACTION,'code_content': $CODE}}}} -# ``` - -# 按照以下思维链格式进行回应($JSON_BLOB要求符合上述规定): -# 问题:输入问题以回答 -# 思考:考虑之前和之后的步骤 -# 行动: -# ``` -# $JSON_BLOB -# ``` -# 观察:行动结果 -# ...(重复思考/行动/观察N次) -# 思考:我知道该如何回应 -# 行动: -# ``` -# $JSON_BLOB -# ``` -# """ \ No newline at end of file diff --git a/coagent/connector/configs/prompts/react_tool_prompt.py b/coagent/connector/configs/prompts/react_tool_prompt.py deleted file mode 100644 index ad029c7..0000000 --- a/coagent/connector/configs/prompts/react_tool_prompt.py +++ /dev/null @@ -1,77 +0,0 @@ -REACT_TOOL_PROMPT = """#### Agent Profile - -When interacting with users, your role is to respond in a helpful and accurate manner using the tools available. Follow the steps below to ensure efficient and effective use of the tools. - -Please note that all the tools you can use are listed below. You can only choose from these tools for use. - -If there are no suitable tools, please do not invent any tools. Just let the user know that you do not have suitable tools to use. - -ATTENTION: The Action Status field ensures that the tools or code mentioned in the Action can be parsed smoothly. Please make sure not to omit the Action Status field when replying. - -#### Response Output Format - -**Thoughts:** According the previous observations, plan the approach for using the tool effectively. - -**Action Status:** Set to either 'stopped' or 'tool_using'. If 'stopped', provide the final response to the original question. If 'tool_using', proceed with using the specified tool. - -**Action:** Use the tools by formatting the tool action in JSON. The format should be: - -```json -{ - "tool_name": "$TOOL_NAME", - "tool_params": "$INPUT" -} -``` - -**Observation:** Evaluate the outcome of the tool's usage. - -... (Repeat this Thoughts/Action Status/Action/Observation cycle as needed) - -**Thoughts:** Determine the final response based on the results. - -**Action Status:** Set to 'stopped' - -**Action:** Conclude with the final response to the original question in this format: - -```json -{ - "tool_params": "Final response to be provided to the user", - "tool_name": "notool", -} -``` -""" - - -# REACT_TOOL_PROMPT = """尽可能地以有帮助和准确的方式回应人类。您可以使用以下工具: -# {formatted_tools} -# 使用json blob来指定一个工具,提供一个action关键字(工具名称)和一个tool_params关键字(工具输入)。 -# 有效的"action"值为:"stopped" 或 "tool_using" (使用工具来回答问题) -# 有效的"tool_name"值为:{tool_names} -# 请仅在每个$JSON_BLOB中提供一个action,如下所示: -# ``` -# {{{{ -# "action": $ACTION, -# "tool_name": $TOOL_NAME, -# "tool_params": $INPUT -# }}}} -# ``` - -# 按照以下格式进行回应: -# 问题:输入问题以回答 -# 思考:考虑之前和之后的步骤 -# 行动: -# ``` -# $JSON_BLOB -# ``` -# 观察:行动结果 -# ...(重复思考/行动/观察N次) -# 思考:我知道该如何回应 -# 行动: -# ``` -# {{{{ -# "action": "stopped", -# "tool_name": "notool", -# "tool_params": "最终返回答案给到用户" -# }}}} -# ``` -# """ diff --git a/coagent/connector/configs/prompts/refine_template_prompt.py b/coagent/connector/configs/prompts/refine_template_prompt.py deleted file mode 100644 index 5b52cad..0000000 --- a/coagent/connector/configs/prompts/refine_template_prompt.py +++ /dev/null @@ -1,30 +0,0 @@ -REFINE_TEMPLATE_PROMPT = """#### Agent Profile - -When users have a sequence of tasks that require optimization or adjustment based on feedback from the context, your role is to refine the existing plan. -Your task is to identify where improvements can be made and provide a revised plan that is more efficient or effective. -Each instruction should be an enhancement of the existing plan and should specify the step from which the changes should be implemented. - -#### Input Format - -**Context:** Review the history of the plan and feedback to identify areas for improvement. -Take into consideration all feedback information from the current step. If there is no existing plan, generate a new one. - -#### Response Output Format - -**REASON:** think the reason of why choose 'finished', 'unchanged' or 'adjusted' step by step. - -**Action Status:** Set to 'finished', 'unchanged' or 'adjusted'. -If it's 'finished', all tasks are accomplished, and no adjustments are needed, so PLAN_STEP is set to -1. -If it's 'unchanged', this PLAN has no problem, just set PLAN_STEP to CURRENT_STEP+1. -If it's 'adjusted', the PLAN is to provide an optimized version of the original plan. - -**PLAN:** -```list -[ - "First, we should ...", -] -``` - -**PLAN_STEP:** Set to the plan index from which the changes should start. Index range from 0 to n-1 or -1 -If it's 'finished', the PLAN_STEP is -1. If it's 'adjusted', the PLAN_STEP is the index of the first revised task in the sequence. -""" \ No newline at end of file diff --git a/coagent/connector/configs/prompts/summary_template_prompt.py b/coagent/connector/configs/prompts/summary_template_prompt.py deleted file mode 100644 index 8f7c9aa..0000000 --- a/coagent/connector/configs/prompts/summary_template_prompt.py +++ /dev/null @@ -1,40 +0,0 @@ -CONV_SUMMARY_PROMPT = """尽可能地以有帮助和准确的方式回应人类,根据“背景信息”中的有效信息回答问题, -使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)。 -有效的 'action' 值为:'finished'(任务已经可以通过上下文信息可以回答) or 'continue' (根据背景信息回答问题)。 -在每个 $JSON_BLOB 中仅提供一个 action,如下所示: -``` -{{'action': $ACTION, 'content': '根据背景信息回答问题'}} -``` -按照以下格式进行回应: -问题:输入问题以回答 -行动: -``` -$JSON_BLOB -``` -""" - -CONV_SUMMARY_PROMPT = """尽可能地以有帮助和准确的方式回应人类 -根据“背景信息”中的有效信息回答问题,同时展现解答的过程和内容 -若能根“背景信息”回答问题,则直接回答 -否则,总结“背景信息”的内容 -""" - - -CONV_SUMMARY_PROMPT_SPEC = """ -Your job is to summarize a history of previous messages in a conversation between an AI persona and a human. -The conversation you are given is a fixed context window and may not be complete. -Messages sent by the AI are marked with the 'assistant' role. -The AI 'assistant' can also make calls to functions, whose outputs can be seen in messages with the 'function' role. -Things the AI says in the message content are considered inner monologue and are not seen by the user. -The only AI messages seen by the user are from when the AI uses 'send_message'. -Messages the user sends are in the 'user' role. -The 'user' role is also used for important system events, such as login events and heartbeat events (heartbeats run the AI's program without user action, allowing the AI to act without prompting from the user sending them a message). -Summarize what happened in the conversation from the perspective of the AI (use the first person). -Keep your summary less than 100 words, do NOT exceed this word limit. -Only output the summary, do NOT include anything else in your output. - ---- conversation -{conversation} ---- - -""" \ No newline at end of file diff --git a/coagent/connector/memory_manager.py b/coagent/connector/memory_manager.py deleted file mode 100644 index 3ccd698..0000000 --- a/coagent/connector/memory_manager.py +++ /dev/null @@ -1,489 +0,0 @@ -from abc import abstractmethod, ABC -from typing import List, Dict -import os, sys, copy, json -from jieba.analyse import extract_tags -from collections import Counter -from loguru import logger - -from langchain.docstore.document import Document - - -from .schema import Memory, Message -from coagent.service.service_factory import KBServiceFactory -from coagent.llm_models import getChatModelFromConfig -from coagent.llm_models.llm_config import EmbedConfig, LLMConfig -from coagent.embeddings.utils import load_embeddings_from_path -from coagent.utils.common_utils import save_to_json_file, read_json_file, addMinutesToTime -from coagent.connector.configs.prompts import CONV_SUMMARY_PROMPT_SPEC -from coagent.orm import table_init -from coagent.base_configs.env_config import KB_ROOT_PATH -# from configs.model_config import KB_ROOT_PATH, EMBEDDING_MODEL, EMBEDDING_DEVICE, SCORE_THRESHOLD -# from configs.model_config import embedding_model_dict - - -class BaseMemoryManager(ABC): - """ - This class represents a local memory manager that inherits from BaseMemoryManager. - - Attributes: - - user_name: A string representing the user name. Default is "default". - - unique_name: A string representing the unique name. Default is "default". - - memory_type: A string representing the memory type. Default is "recall". - - do_init: A boolean indicating whether to initialize. Default is False. - - current_memory: An instance of Memory class representing the current memory. - - recall_memory: An instance of Memory class representing the recall memory. - - summary_memory: An instance of Memory class representing the summary memory. - - save_message_keys: A list of strings representing the keys for saving messages. - - Methods: - - __init__: Initializes the LocalMemoryManager with the given user_name, unique_name, memory_type, and do_init. - - init_vb: Initializes the vb. - - append: Appends a message to the recall memory, current memory, and summary memory. - - extend: Extends the recall memory, current memory, and summary memory. - - save: Saves the memory to the specified directory. - - load: Loads the memory from the specified directory and returns a Memory instance. - - save_new_to_vs: Saves new messages to the vector space. - - save_to_vs: Saves the memory to the vector space. - - router_retrieval: Routes the retrieval based on the retrieval type. - - embedding_retrieval: Retrieves messages based on embedding. - - text_retrieval: Retrieves messages based on text. - - datetime_retrieval: Retrieves messages based on datetime. - - recursive_summary: Performs recursive summarization of messages. - """ - - def __init__( - self, - user_name: str = "default", - unique_name: str = "default", - memory_type: str = "recall", - do_init: bool = False, - ): - """ - Initializes the LocalMemoryManager with the given parameters. - - Args: - - user_name: A string representing the user name. Default is "default". - - unique_name: A string representing the unique name. Default is "default". - - memory_type: A string representing the memory type. Default is "recall". - - do_init: A boolean indicating whether to initialize. Default is False. - """ - self.user_name = user_name - self.unique_name = unique_name - self.memory_type = memory_type - self.do_init = do_init - # self.current_memory = Memory(messages=[]) - # self.recall_memory = Memory(messages=[]) - # self.summary_memory = Memory(messages=[]) - self.current_memory_dict: Dict[str, Memory] = {} - self.recall_memory_dict: Dict[str, Memory] = {} - self.summary_memory_dict: Dict[str, Memory] = {} - self.save_message_keys = [ - 'chat_index', 'role_name', 'role_type', 'role_prompt', 'input_query', 'origin_query', - 'datetime', 'role_content', 'step_content', 'parsed_output', 'spec_parsed_output', 'parsed_output_list', - 'task', 'db_docs', 'code_docs', 'search_docs', 'phase_name', 'chain_name', 'customed_kargs'] - self.init_vb() - - def re_init(self, do_init: bool=False): - self.init_vb() - - def init_vb(self, do_init: bool=None): - """ - Initializes the vb. - """ - pass - - def append(self, message: Message): - """ - Appends a message to the recall memory, current memory, and summary memory. - - Args: - - message: An instance of Message class representing the message to be appended. - """ - pass - - def extend(self, memory: Memory): - """ - Extends the recall memory, current memory, and summary memory. - - Args: - - memory: An instance of Memory class representing the memory to be extended. - """ - pass - - def save(self, save_dir: str = ""): - """ - Saves the memory to the specified directory. - - Args: - - save_dir: A string representing the directory to save the memory. Default is KB_ROOT_PATH. - """ - pass - - def load(self, load_dir: str = "") -> Memory: - """ - Loads the memory from the specified directory and returns a Memory instance. - - Args: - - load_dir: A string representing the directory to load the memory from. Default is KB_ROOT_PATH. - - Returns: - - An instance of Memory class representing the loaded memory. - """ - pass - - def save_new_to_vs(self, messages: List[Message]): - """ - Saves new messages to the vector space. - - Args: - - messages: A list of Message instances representing the messages to be saved. - - embed_model: A string representing the embedding model. Default is EMBEDDING_MODEL. - - embed_device: A string representing the embedding device. Default is EMBEDDING_DEVICE. - """ - pass - - def save_to_vs(self, ): - """ - Saves the memory to the vector space. - """ - pass - - def get_memory_pool(self, user_name: str, ): - """ - return memory_pool - """ - pass - - def router_retrieval(self, text: str=None, datetime: str = None, n=5, top_k=5, retrieval_type: str = "embedding", **kwargs) -> List[Message]: - """ - Routes the retrieval based on the retrieval type. - - Args: - - text: A string representing the text for retrieval. Default is None. - - datetime: A string representing the datetime for retrieval. Default is None. - - n: An integer representing the number of messages. Default is 5. - - top_k: An integer representing the top k messages. Default is 5. - - retrieval_type: A string representing the retrieval type. Default is "embedding". - - **kwargs: Additional keyword arguments for retrieval. - - Returns: - - A list of Message instances representing the retrieved messages. - """ - pass - - def embedding_retrieval(self, text: str, embed_model="", top_k=1, score_threshold=1.0, **kwargs) -> List[Message]: - """ - Retrieves messages based on embedding. - - Args: - - text: A string representing the text for retrieval. - - embed_model: A string representing the embedding model. Default is EMBEDDING_MODEL. - - top_k: An integer representing the top k messages. Default is 1. - - score_threshold: A float representing the score threshold. Default is SCORE_THRESHOLD. - - **kwargs: Additional keyword arguments for retrieval. - - Returns: - - A list of Message instances representing the retrieved messages. - """ - pass - - def text_retrieval(self, text: str, **kwargs) -> List[Message]: - """ - Retrieves messages based on text. - - Args: - - text: A string representing the text for retrieval. - - **kwargs: Additional keyword arguments for retrieval. - - Returns: - - A list of Message instances representing the retrieved messages. - """ - pass - - def datetime_retrieval(self, datetime: str, text: str = None, n: int = 5, **kwargs) -> List[Message]: - """ - Retrieves messages based on datetime. - - Args: - - datetime: A string representing the datetime for retrieval. - - text: A string representing the text for retrieval. Default is None. - - n: An integer representing the number of messages. Default is 5. - - **kwargs: Additional keyword arguments for retrieval. - - Returns: - - A list of Message instances representing the retrieved messages. - """ - pass - - def recursive_summary(self, messages: List[Message], split_n: int = 20) -> List[Message]: - """ - Performs recursive summarization of messages. - - Args: - - messages: A list of Message instances representing the messages to be summarized. - - split_n: An integer representing the split n. Default is 20. - - Returns: - - A list of Message instances representing the summarized messages. - """ - pass - - -class LocalMemoryManager(BaseMemoryManager): - - def __init__( - self, - embed_config: EmbedConfig, - llm_config: LLMConfig, - user_name: str = "default", - unique_name: str = "default", - memory_type: str = "recall", - do_init: bool = False, - kb_root_path: str = KB_ROOT_PATH, - ): - self.user_name = user_name - self.unique_name = unique_name - self.memory_type = memory_type - self.do_init = do_init - self.kb_root_path = kb_root_path - self.embed_config: EmbedConfig = embed_config - self.llm_config: LLMConfig = llm_config - # self.current_memory = Memory(messages=[]) - # self.recall_memory = Memory(messages=[]) - # self.summary_memory = Memory(messages=[]) - self.current_memory_dict: Dict[str, Memory] = {} - self.recall_memory_dict: Dict[str, Memory] = {} - self.summary_memory_dict: Dict[str, Memory] = {} - self.save_message_keys = [ - 'chat_index', 'role_name', 'role_type', 'role_prompt', 'input_query', 'origin_query', - 'datetime', 'role_content', 'step_content', 'parsed_output', 'spec_parsed_output', 'parsed_output_list', - 'task', 'db_docs', 'code_docs', 'search_docs', 'phase_name', 'chain_name', 'customed_kargs'] - self.init_vb() - - def re_init(self, do_init: bool=False): - self.init_vb(do_init) - - def init_vb(self, do_init: bool=None): - vb_name = f"{self.user_name}/{self.unique_name}/{self.memory_type}" - # default to recreate a new vb - table_init() - vb = KBServiceFactory.get_service_by_name(vb_name, self.embed_config, self.kb_root_path) - if vb: - status = vb.clear_vs() - - check_do_init = do_init if do_init else self.do_init - if not check_do_init: - self.load(self.kb_root_path) - self.save_to_vs() - - def append(self, message: Message): - self.check_user_name(message.user_name) - - uuid_name = "_".join([self.user_name, self.unique_name, self.memory_type]) - self.recall_memory_dict[uuid_name].append(message) - # - if message.role_type == "summary": - self.summary_memory_dict[uuid_name].append(message) - else: - self.current_memory_dict[uuid_name].append(message) - - self.save(self.kb_root_path) - self.save_new_to_vs([message]) - - # def extend(self, memory: Memory): - # self.recall_memory.extend(memory) - # self.current_memory.extend(self.recall_memory.filter_by_role_type(["summary"])) - # self.summary_memory.extend(self.recall_memory.select_by_role_type(["summary"])) - # self.save(self.kb_root_path) - # self.save_new_to_vs(memory.messages) - - def save(self, save_dir: str = "./"): - file_path = os.path.join(save_dir, f"{self.user_name}/{self.unique_name}/{self.memory_type}/converation.jsonl") - uuid_name = "_".join([self.user_name, self.unique_name, self.memory_type]) - - memory_messages = self.recall_memory_dict[uuid_name].dict() - memory_messages = {k: [ - {kkk: vvv for kkk, vvv in vv.items() if kkk in self.save_message_keys} - for vv in v ] - for k, v in memory_messages.items() - } - # - save_to_json_file(memory_messages, file_path) - - def load(self, load_dir: str = None) -> Memory: - load_dir = load_dir or self.kb_root_path - file_path = os.path.join(load_dir, f"{self.user_name}/{self.unique_name}/{self.memory_type}/converation.jsonl") - uuid_name = "_".join([self.user_name, self.unique_name, self.memory_type]) - - if os.path.exists(file_path): - # self.recall_memory = Memory(**read_json_file(file_path)) - # self.current_memory = Memory(messages=self.recall_memory.filter_by_role_type(["summary"])) - # self.summary_memory = Memory(messages=self.recall_memory.select_by_role_type(["summary"])) - - recall_memory = Memory(**read_json_file(file_path)) - self.recall_memory_dict[uuid_name] = recall_memory - self.current_memory_dict[uuid_name] = Memory(messages=recall_memory.filter_by_role_type(["summary"])) - self.summary_memory_dict[uuid_name] = Memory(messages=recall_memory.select_by_role_type(["summary"])) - else: - self.recall_memory_dict[uuid_name] = Memory(messages=[]) - self.current_memory_dict[uuid_name] = Memory(messages=[]) - self.summary_memory_dict[uuid_name] = Memory(messages=[]) - - def save_new_to_vs(self, messages: List[Message]): - if self.embed_config: - vb_name = f"{self.user_name}/{self.unique_name}/{self.memory_type}" - # default to faiss, todo: add new vstype - vb = KBServiceFactory.get_service(vb_name, "faiss", self.embed_config, self.kb_root_path) - embeddings = load_embeddings_from_path(self.embed_config.embed_model_path, self.embed_config.model_device, self.embed_config.langchain_embeddings) - messages = [ - {k: v for k, v in m.dict().items() if k in self.save_message_keys} - for m in messages] - docs = [{"page_content": m["step_content"] or m["role_content"] or m["input_query"] or m["origin_query"], "metadata": m} for m in messages] - docs = [Document(**doc) for doc in docs] - vb.do_add_doc(docs, embeddings) - - def save_to_vs(self): - '''only after load''' - if self.embed_config: - vb_name = f"{self.user_name}/{self.unique_name}/{self.memory_type}" - uuid_name = "_".join([self.user_name, self.unique_name, self.memory_type]) - # default to recreate a new vb - vb = KBServiceFactory.get_service_by_name(vb_name, self.embed_config, self.kb_root_path) - if vb: - status = vb.clear_vs() - # create_kb(vb_name, "faiss", embed_model) - - # default to faiss, todo: add new vstype - vb = KBServiceFactory.get_service(vb_name, "faiss", self.embed_config, self.kb_root_path) - embeddings = load_embeddings_from_path(self.embed_config.embed_model_path, self.embed_config.model_device, self.embed_config.langchain_embeddings) - messages = self.recall_memory_dict[uuid_name].dict() - messages = [ - {kkk: vvv for kkk, vvv in vv.items() if kkk in self.save_message_keys} - for k, v in messages.items() for vv in v] - docs = [{"page_content": m["step_content"] or m["role_content"] or m["input_query"] or m["origin_query"], "metadata": m} for m in messages] - docs = [Document(**doc) for doc in docs] - vb.do_add_doc(docs, embeddings) - - # def load_from_vs(self, embed_model=EMBEDDING_MODEL) -> Memory: - # vb_name = f"{self.user_name}/{self.unique_name}/{self.memory_type}" - - # create_kb(vb_name, "faiss", embed_model) - # # default to faiss, todo: add new vstype - # vb = KBServiceFactory.get_service(vb_name, "faiss", embed_model) - # docs = vb.get_all_documents() - # print(docs) - - def get_memory_pool(self, user_name: str, ): - self.check_user_name(user_name) - uuid_name = "_".join([self.user_name, self.unique_name, self.memory_type]) - return self.recall_memory_dict[uuid_name] - - def router_retrieval(self, user_name: str = "default", text: str=None, datetime: str = None, n=5, top_k=5, retrieval_type: str = "embedding", **kwargs) -> List[Message]: - retrieval_func_dict = { - "embedding": self.embedding_retrieval, "text": self.text_retrieval, "datetime": self.datetime_retrieval - } - - # 确保提供了合法的检索类型 - if retrieval_type not in retrieval_func_dict: - raise ValueError(f"Invalid retrieval_type: '{retrieval_type}'. Available types: {list(retrieval_func_dict.keys())}") - - retrieval_func = retrieval_func_dict[retrieval_type] - # - params = locals() - params.pop("self") - params.pop("retrieval_type") - params.update(params.pop('kwargs', {})) - # - return retrieval_func(**params) - - def embedding_retrieval(self, text: str, top_k=1, score_threshold=1.0, user_name: str = "default", **kwargs) -> List[Message]: - if text is None: return [] - vb_name = f"{user_name}/{self.unique_name}/{self.memory_type}" - # logger.debug(f"vb_name={vb_name}") - vb = KBServiceFactory.get_service(vb_name, "faiss", self.embed_config, self.kb_root_path) - docs = vb.search_docs(text, top_k=top_k, score_threshold=score_threshold) - return [Message(**doc.metadata) for doc, score in docs] - - def text_retrieval(self, text: str, user_name: str = "default", **kwargs) -> List[Message]: - if text is None: return [] - uuid_name = "_".join([user_name, self.unique_name, self.memory_type]) - # logger.debug(f"uuid_name={uuid_name}") - return self._text_retrieval_from_cache(self.recall_memory_dict[uuid_name].messages, text, score_threshold=0.3, topK=5, **kwargs) - - def datetime_retrieval(self, datetime: str, text: str = None, n: int = 5, user_name: str = "default", **kwargs) -> List[Message]: - if datetime is None: return [] - uuid_name = "_".join([user_name, self.unique_name, self.memory_type]) - # logger.debug(f"uuid_name={uuid_name}") - return self._datetime_retrieval_from_cache(self.recall_memory_dict[uuid_name].messages, datetime, text, n, **kwargs) - - def _text_retrieval_from_cache(self, messages: List[Message], text: str = None, score_threshold=0.3, topK=5, tag_topK=5, **kwargs) -> List[Message]: - keywords = extract_tags(text, topK=tag_topK) - - matched_messages = [] - for message in messages: - message_keywords = extract_tags(message.step_content or message.role_content or message.input_query, topK=tag_topK) - # calculate jaccard similarity - intersection = Counter(keywords) & Counter(message_keywords) - union = Counter(keywords) | Counter(message_keywords) - similarity = sum(intersection.values()) / sum(union.values()) - if similarity >= score_threshold: - matched_messages.append((message, similarity)) - matched_messages = sorted(matched_messages, key=lambda x:x[1]) - return [m for m, s in matched_messages][:topK] - - def _datetime_retrieval_from_cache(self, messages: List[Message], datetime: str, text: str = None, n: int = 5, **kwargs) -> List[Message]: - # select message by datetime - datetime_before, datetime_after = addMinutesToTime(datetime, n) - select_messages = [ - message for message in messages - if datetime_before<=message.datetime<=datetime_after - ] - return self._text_retrieval_from_cache(select_messages, text) - - def recursive_summary(self, messages: List[Message], split_n: int = 20) -> List[Message]: - - if len(messages) == 0: - return messages - - newest_messages = messages[-split_n:] - summary_messages = messages[:len(messages)-split_n] - - while (len(newest_messages) != 0) and (newest_messages[0].role_type != "user"): - message = newest_messages.pop(0) - summary_messages.append(message) - - # summary - # model = getChatModel(temperature=0.2) - model = getChatModelFromConfig(self.llm_config) - summary_content = '\n\n'.join([ - m.role_type + "\n" + "\n".join(([f"*{k}* {v}" for parsed_output in m.parsed_output_list for k, v in parsed_output.items() if k not in ['Action Status']])) - for m in summary_messages if m.role_type not in ["summary"] - ]) - - summary_prompt = CONV_SUMMARY_PROMPT_SPEC.format(conversation=summary_content) - content = model.predict(summary_prompt) - summary_message = Message( - role_name="summaryer", - role_type="summary", - role_content=content, - step_content=content, - parsed_output_list=[], - customed_kargs={} - ) - summary_message.parsed_output_list.append({"summary": content}) - newest_messages.insert(0, summary_message) - return newest_messages - - def check_user_name(self, user_name: str): - # logger.debug(f"self.user_name is {self.user_name}") - if user_name != self.user_name: - self.user_name = user_name - self.init_vb() - - uuid_name = "_".join([self.user_name, self.unique_name, self.memory_type]) - if uuid_name not in self.recall_memory_dict: - self.recall_memory_dict[uuid_name] = Memory(messages=[]) - self.current_memory_dict[uuid_name] = Memory(messages=[]) - self.summary_memory_dict[uuid_name] = Memory(messages=[]) - - # logger.debug(f"self.user_name is {self.user_name}") \ No newline at end of file diff --git a/coagent/connector/message_process.py b/coagent/connector/message_process.py deleted file mode 100644 index b225fe5..0000000 --- a/coagent/connector/message_process.py +++ /dev/null @@ -1,306 +0,0 @@ -import re, traceback, uuid, copy, json, os -from typing import Union -from loguru import logger - -from langchain.schema import BaseRetriever - -from coagent.connector.schema import ( - Memory, Role, Message, ActionStatus, CodeDoc, Doc, LogVerboseEnum -) -from coagent.retrieval.base_retrieval import IMRertrieval -from coagent.connector.memory_manager import BaseMemoryManager -from coagent.tools import DDGSTool, DocRetrieval, CodeRetrieval -from coagent.sandbox import PyCodeBox, CodeBoxResponse -from coagent.llm_models.llm_config import LLMConfig, EmbedConfig -from coagent.base_configs.env_config import JUPYTER_WORK_PATH - -from .utils import parse_dict_to_dict, parse_text_to_dict - - -class MessageUtils: - def __init__( - self, - role: Role = None, - sandbox_server: dict = {}, - jupyter_work_path: str = JUPYTER_WORK_PATH, - embed_config: EmbedConfig = None, - llm_config: LLMConfig = None, - kb_root_path: str = "", - doc_retrieval: Union[BaseRetriever, IMRertrieval] = None, - code_retrieval: IMRertrieval = None, - search_retrieval: IMRertrieval = None, - log_verbose: str = "0" - ) -> None: - self.role = role - self.sandbox_server = sandbox_server - self.jupyter_work_path = jupyter_work_path - self.embed_config = embed_config - self.llm_config = llm_config - self.kb_root_path = kb_root_path - self.doc_retrieval = doc_retrieval - self.code_retrieval = code_retrieval - self.search_retrieval = search_retrieval - self.codebox = PyCodeBox( - remote_url=self.sandbox_server.get("url", "http://127.0.0.1:5050"), - remote_ip=self.sandbox_server.get("host", "http://127.0.0.1"), - remote_port=self.sandbox_server.get("port", "5050"), - jupyter_work_path=jupyter_work_path, - token="mytoken", - do_code_exe=True, - do_remote=self.sandbox_server.get("do_remote", False), - do_check_net=False - ) - self.log_verbose = os.environ.get("log_verbose", "0") or log_verbose - - def inherit_extrainfo(self, input_message: Message, output_message: Message): - output_message.user_name = input_message.user_name - output_message.db_docs = input_message.db_docs - output_message.search_docs = input_message.search_docs - output_message.code_docs = input_message.code_docs - output_message.figures.update(input_message.figures) - output_message.origin_query = input_message.origin_query - output_message.code_engine_name = input_message.code_engine_name - - output_message.doc_engine_name = input_message.doc_engine_name - output_message.search_engine_name = input_message.search_engine_name - output_message.top_k = input_message.top_k - output_message.score_threshold = input_message.score_threshold - output_message.cb_search_type = input_message.cb_search_type - output_message.do_doc_retrieval = input_message.do_doc_retrieval - output_message.do_code_retrieval = input_message.do_code_retrieval - output_message.do_tool_retrieval = input_message.do_tool_retrieval - # - output_message.tools = input_message.tools - output_message.agents = input_message.agents - - # update customed_kargs, if exist, keep; else add - customed_kargs = copy.deepcopy(input_message.customed_kargs) - customed_kargs.update(output_message.customed_kargs) - output_message.customed_kargs = customed_kargs - return output_message - - def inherit_baseparam(self, input_message: Message, output_message: Message): - # 只更新参数 - output_message.doc_engine_name = input_message.doc_engine_name - output_message.search_engine_name = input_message.search_engine_name - output_message.top_k = input_message.top_k - output_message.score_threshold = input_message.score_threshold - output_message.cb_search_type = input_message.cb_search_type - output_message.do_doc_retrieval = input_message.do_doc_retrieval - output_message.do_code_retrieval = input_message.do_code_retrieval - output_message.do_tool_retrieval = input_message.do_tool_retrieval - # - output_message.tools = input_message.tools - output_message.agents = input_message.agents - # 存在bug导致相同key被覆盖 - output_message.customed_kargs.update(input_message.customed_kargs) - return output_message - - def get_extrainfo_step(self, message: Message, do_search, do_doc_retrieval, do_code_retrieval, do_tool_retrieval) -> Message: - '''''' - if do_search: - message = self.get_search_retrieval(message) - - if do_doc_retrieval: - message = self.get_doc_retrieval(message) - - if do_code_retrieval: - message = self.get_code_retrieval(message) - - if do_tool_retrieval: - message = self.get_tool_retrieval(message) - - return message - - def get_search_retrieval(self, message: Message,) -> Message: - SEARCH_ENGINES = {"duckduckgo": DDGSTool} - search_docs = [] - for idx, doc in enumerate(SEARCH_ENGINES["duckduckgo"].run(message.role_content, 3)): - doc.update({"index": idx}) - search_docs.append(Doc(**doc)) - message.search_docs = search_docs - return message - - def get_doc_retrieval(self, message: Message) -> Message: - query = message.role_content - knowledge_basename = message.doc_engine_name - top_k = message.top_k - score_threshold = message.score_threshold - if self.doc_retrieval: - if isinstance(self.doc_retrieval, BaseRetriever): - docs = self.doc_retrieval.get_relevant_documents(query) - else: - # docs = self.doc_retrieval.run(query, search_top=message.top_k, score_threshold=message.score_threshold,) - docs = self.doc_retrieval.run(query) - docs = [ - {"index": idx, "snippet": doc.page_content, "title": doc.metadata.get("title_prefix", ""), "link": doc.metadata.get("url", "")} - for idx, doc in enumerate(docs) - ] - message.db_docs = [Doc(**doc) for doc in docs] - else: - if knowledge_basename: - docs = DocRetrieval.run(query, knowledge_basename, top_k, score_threshold, self.embed_config, self.kb_root_path) - message.db_docs = [Doc(**doc) for doc in docs] - return message - - def get_code_retrieval(self, message: Message) -> Message: - query = message.role_content - code_engine_name = message.code_engine_name - history_node_list = message.history_node_list - - use_nh = message.use_nh - local_graph_path = message.local_graph_path - - if self.code_retrieval: - code_docs = self.code_retrieval.run( - query, history_node_list=history_node_list, search_type=message.cb_search_type, - code_limit=1 - ) - else: - code_docs = CodeRetrieval.run(code_engine_name, query, code_limit=message.top_k, history_node_list=history_node_list, search_type=message.cb_search_type, - llm_config=self.llm_config, embed_config=self.embed_config, - use_nh=use_nh, local_graph_path=local_graph_path) - - message.code_docs = [CodeDoc(**doc) for doc in code_docs] - - # related_nodes = [doc.get_related_node() for idx, doc in enumerate(message.code_docs) if idx==0], - # history_node_list.extend([node[0] for node in related_nodes]) - return message - - def get_tool_retrieval(self, message: Message) -> Message: - return message - - def step_router(self, message: Message, history: Memory = None, background: Memory = None, memory_manager: BaseMemoryManager=None) -> tuple[Message, ...]: - '''''' - if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): - logger.info(f"message.action_status: {message.action_status}") - - observation_message = None - if message.action_status == ActionStatus.CODE_EXECUTING: - message, observation_message = self.code_step(message) - elif message.action_status == ActionStatus.TOOL_USING: - message, observation_message = self.tool_step(message) - elif message.action_status == ActionStatus.CODING2FILE: - self.save_code2file(message, self.jupyter_work_path) - elif message.action_status == ActionStatus.CODE_RETRIEVAL: - pass - elif message.action_status == ActionStatus.CODING: - pass - - return message, observation_message - - def code_step(self, message: Message) -> Message: - '''execute code''' - # logger.debug(f"message.role_content: {message.role_content}, message.code_content: {message.code_content}") - code_answer = self.codebox.chat('```python\n{}```'.format(message.code_content)) - code_prompt = f"The return error after executing the above code is {code_answer.code_exe_response},need to recover.\n" \ - if code_answer.code_exe_type == "error" else f"The return information after executing the above code is {code_answer.code_exe_response}.\n" - - observation_message = Message( - user_name=message.user_name, - role_name="observation", - role_type="function", #self.role.role_type, - role_content="", - step_content="", - input_query=message.code_content, - ) - uid = str(uuid.uuid1()) - if code_answer.code_exe_type == "image/png": - message.figures[uid] = code_answer.code_exe_response - message.code_answer = f"\n**Observation:**: The return figure name is {uid} after executing the above code.\n" - message.observation = f"\n**Observation:**: The return figure name is {uid} after executing the above code.\n" - message.step_content += f"\n**Observation:**: The return figure name is {uid} after executing the above code.\n" - # message.role_content += f"\n**Observation:**:执行上述代码后生成一张图片, 图片名为{uid}\n" - observation_message.role_content = f"\n**Observation:**: The return figure name is {uid} after executing the above code.\n" - observation_message.parsed_output = {"Observation": f"The return figure name is {uid} after executing the above code.\n"} - else: - message.code_answer = code_answer.code_exe_response - message.observation = code_answer.code_exe_response - message.step_content += f"\n**Observation:**: {code_prompt}\n" - # message.role_content += f"\n**Observation:**: {code_prompt}\n" - observation_message.role_content = f"\n**Observation:**: {code_prompt}\n" - observation_message.parsed_output = {"Observation": f"{code_prompt}\n"} - - if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): - logger.info(f"**Observation:** {message.action_status}, {message.observation}") - return message, observation_message - - def tool_step(self, message: Message) -> Message: - '''execute tool''' - observation_message = Message( - user_name=message.user_name, - role_name="observation", - role_type="function", #self.role.role_type, - role_content="\n**Observation:** there is no tool can execute\n", - step_content="", - input_query=str(message.tool_params), - tools=message.tools, - ) - if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): - logger.info(f"message: {message.action_status}, {message.tool_params}") - - tool_names = [tool.name for tool in message.tools] - if message.tool_name not in tool_names: - message.tool_answer = "\n**Observation:** there is no tool can execute.\n" - message.observation = "\n**Observation:** there is no tool can execute.\n" - # message.role_content += f"\n**Observation:**: 不存在可以执行的tool\n" - message.step_content += f"\n**Observation:** there is no tool can execute.\n" - observation_message.role_content = f"\n**Observation:** there is no tool can execute.\n" - observation_message.parsed_output = {"Observation": "there is no tool can execute.\n"} - - # logger.debug(message.tool_params) - for tool in message.tools: - if tool.name == message.tool_params.get("tool_name", ""): - tool_res = tool.func(**message.tool_params.get("tool_params", {})) - message.tool_answer = tool_res - message.observation = tool_res - # message.role_content += f"\n**Observation:**: {tool_res}\n" - message.step_content += f"\n**Observation:** {tool_res}.\n" - observation_message.role_content = f"\n**Observation:** {tool_res}.\n" - observation_message.parsed_output = {"Observation": f"{tool_res}.\n"} - break - - if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): - logger.info(f"**Observation:** {message.action_status}, {message.observation}") - return message, observation_message - - def parser(self, message: Message) -> Message: - '''parse llm output into dict''' - content = message.role_content - # parse start - parsed_dict = parse_text_to_dict(content) - spec_parsed_dict = parse_dict_to_dict(parsed_dict) - # select parse value - action_value = parsed_dict.get('Action Status') - if action_value: - action_value = action_value.lower() - - code_content_value = spec_parsed_dict.get('code') - if action_value == 'tool_using': - tool_params_value = spec_parsed_dict.get('json') - else: - tool_params_value = None - - # add parse value to message - message.action_status = action_value or "default" - message.code_content = code_content_value - message.tool_params = tool_params_value - message.parsed_output = parsed_dict - message.spec_parsed_output = spec_parsed_dict - return message - - def save_code2file(self, message: Message, project_dir="./"): - filename = message.parsed_output.get("SaveFileName") - code = message.spec_parsed_output.get("code") - - for k, v in {">": ">", "≥": ">=", "<": "<", "≤": "<="}.items(): - code = code.replace(k, v) - - file_path = os.path.join(project_dir, filename) - - if not os.path.exists(file_path): - os.makedirs(os.path.dirname(file_path), exist_ok=True) - - with open(file_path, "w") as f: - f.write(code) - \ No newline at end of file diff --git a/coagent/connector/phase/__init__.py b/coagent/connector/phase/__init__.py deleted file mode 100644 index 73332dc..0000000 --- a/coagent/connector/phase/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .base_phase import BasePhase - -__all__ = ["BasePhase"] \ No newline at end of file diff --git a/coagent/connector/phase/base_phase.py b/coagent/connector/phase/base_phase.py deleted file mode 100644 index 0b71e98..0000000 --- a/coagent/connector/phase/base_phase.py +++ /dev/null @@ -1,272 +0,0 @@ -from typing import List, Union, Dict, Tuple -import os -import json -import importlib -import copy -from loguru import logger - -from langchain.schema import BaseRetriever - -from coagent.connector.agents import BaseAgent -from coagent.connector.chains import BaseChain -from coagent.connector.schema import ( - Memory, Task, Message, AgentConfig, ChainConfig, PhaseConfig, LogVerboseEnum, - CompletePhaseConfig, - load_chain_configs, load_phase_configs, load_role_configs -) -from coagent.connector.memory_manager import BaseMemoryManager, LocalMemoryManager -from coagent.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS -from coagent.connector.message_process import MessageUtils -from coagent.llm_models.llm_config import EmbedConfig, LLMConfig -from coagent.base_configs.env_config import JUPYTER_WORK_PATH, KB_ROOT_PATH - - -role_configs = load_role_configs(AGETN_CONFIGS) -chain_configs = load_chain_configs(CHAIN_CONFIGS) -phase_configs = load_phase_configs(PHASE_CONFIGS) - - -CUR_DIR = os.path.dirname(os.path.abspath(__file__)) - - -class BasePhase: - - def __init__( - self, - phase_name: str, - phase_config: CompletePhaseConfig = None, - kb_root_path: str = KB_ROOT_PATH, - jupyter_work_path: str = JUPYTER_WORK_PATH, - sandbox_server: dict = {}, - embed_config: EmbedConfig = None, - llm_config: LLMConfig = None, - task: Task = None, - base_phase_config: Union[dict, str] = PHASE_CONFIGS, - base_chain_config: Union[dict, str] = CHAIN_CONFIGS, - base_role_config: Union[dict, str] = AGETN_CONFIGS, - chains: List[BaseChain] = [], - doc_retrieval: Union[BaseRetriever] = None, - code_retrieval = None, - search_retrieval = None, - log_verbose: str = "0" - ) -> None: - # - self.phase_name = phase_name - self.do_summary = False - self.do_search = search_retrieval is not None - self.do_code_retrieval = code_retrieval is not None - self.do_doc_retrieval = doc_retrieval is not None - self.do_tool_retrieval = False - # memory_pool dont have specific order - # self.memory_pool = Memory(messages=[]) - self.embed_config = embed_config - self.llm_config = llm_config - self.sandbox_server = sandbox_server - self.jupyter_work_path = jupyter_work_path - self.kb_root_path = kb_root_path - self.log_verbose = max(os.environ.get("log_verbose", "0"), log_verbose) - # TODO透传 - self.doc_retrieval = doc_retrieval - self.code_retrieval = code_retrieval - self.search_retrieval = search_retrieval - self.message_utils = MessageUtils(None, sandbox_server, jupyter_work_path, embed_config, llm_config, kb_root_path, doc_retrieval, code_retrieval, search_retrieval, log_verbose) - self.global_memory = Memory(messages=[]) - self.phase_memory: List[Memory] = [] - # according phase name to init the phase contains - self.chains: List[BaseChain] = chains if chains else self.init_chains( - phase_name, - phase_config, - task=task, - memory=None, - base_phase_config = base_phase_config, - base_chain_config = base_chain_config, - base_role_config = base_role_config, - ) - self.memory_manager: BaseMemoryManager = LocalMemoryManager( - unique_name=phase_name, do_init=True, kb_root_path = kb_root_path, embed_config=embed_config, llm_config=llm_config - ) - self.conv_summary_agent = BaseAgent( - role=role_configs["conv_summary"].role, - prompt_config=role_configs["conv_summary"].prompt_config, - task = None, memory = None, - llm_config=self.llm_config, - embed_config=self.embed_config, - sandbox_server=sandbox_server, - jupyter_work_path=jupyter_work_path, - kb_root_path=kb_root_path - ) - - def astep(self, query: Message, history: Memory = None, reinit_memory=False) -> Tuple[Message, Memory]: - if reinit_memory: - self.memory_manager.re_init(reinit_memory) - self.memory_manager.append(query) - summary_message = None - chain_message = Memory(messages=[]) - local_phase_memory = Memory(messages=[]) - # do_search、do_doc_search、do_code_search - query = self.message_utils.get_extrainfo_step(query, self.do_search, self.do_doc_retrieval, self.do_code_retrieval, self.do_tool_retrieval) - query.parsed_output = query.parsed_output if query.parsed_output else {"origin_query": query.input_query} - query.parsed_output_list = query.parsed_output_list if query.parsed_output_list else [{"origin_query": query.input_query}] - input_message = copy.deepcopy(query) - - self.global_memory.append(input_message) - local_phase_memory.append(input_message) - for chain in self.chains: - # chain can supply background and query to next chain - for output_message, local_chain_memory in chain.astep(input_message, history, background=chain_message, memory_manager=self.memory_manager): - # logger.debug(f"local_memory: {local_phase_memory + local_chain_memory}") - yield output_message, local_phase_memory + local_chain_memory - - output_message = self.message_utils.inherit_extrainfo(input_message, output_message) - input_message = output_message - # logger.info(f"{chain.chainConfig.chain_name} phase_step: {output_message.role_content}") - # 这一段也有问题 - self.global_memory.extend(local_chain_memory) - local_phase_memory.extend(local_chain_memory) - - # whether to use summary_llm - if self.do_summary: - if LogVerboseEnum.ge(LogVerboseEnum.Log1Level, self.log_verbose): - logger.info(f"{self.conv_summary_agent.role.role_name} input global memory: {local_phase_memory.to_str_messages(content_key='step_content')}") - for summary_message in self.conv_summary_agent.astep(query, background=local_phase_memory, memory_manager=self.memory_manager): - pass - # summary_message = Message(**summary_message) - summary_message.role_name = chain.chainConfig.chain_name - summary_message = self.conv_summary_agent.message_utils.parser(summary_message) - summary_message = self.message_utils.inherit_extrainfo(output_message, summary_message) - chain_message.append(summary_message) - - message = summary_message or output_message - yield message, local_phase_memory - - # 由于不会存在多轮chain执行,所以直接保留memory即可 - for chain in self.chains: - self.phase_memory.append(chain.global_memory) - # TODO:local_memory缺少添加summary的过程 - message = summary_message or output_message - message.role_name = self.phase_name - yield message, local_phase_memory - - def step(self, query: Message, history: Memory = None, reinit_memory=False) -> Tuple[Message, Memory]: - for message, local_phase_memory in self.astep(query, history=history, reinit_memory=reinit_memory): - pass - return message, local_phase_memory - - def pre_print(self, query, history: Memory = None) -> List[str]: - chain_message = Memory(messages=[]) - for chain in self.chains: - chain.pre_print(query, history, background=chain_message, memory_manager=self.memory_manager) - - def init_chains(self, phase_name: str, phase_config: CompletePhaseConfig, base_phase_config, base_chain_config, - base_role_config, task=None, memory=None) -> List[BaseChain]: - # load config - role_configs = load_role_configs(base_role_config) - chain_configs = load_chain_configs(base_chain_config) - phase_configs = load_phase_configs(base_phase_config) - - chains = [] - self.chain_module = importlib.import_module("coagent.connector.chains") - self.agent_module = importlib.import_module("coagent.connector.agents") - - phase: PhaseConfig = phase_configs.get(phase_name) - # set phase - self.do_summary = phase.do_summary - self.do_search = phase.do_search - self.do_code_retrieval = phase.do_code_retrieval - self.do_doc_retrieval = phase.do_doc_retrieval - self.do_tool_retrieval = phase.do_tool_retrieval - logger.info(f"start to init the phase, the phase_name is {phase_name}, it contains these chains such as {phase.chains}") - - for chain_name in phase.chains: - # logger.debug(f"{chain_configs.keys()}") - chain_config: ChainConfig = chain_configs[chain_name] - logger.info(f"start to init the chain, the chain_name is {chain_name}, it contains these agents such as {chain_config.agents}") - - agents = [] - for agent_name in chain_config.agents: - agent_config: AgentConfig = role_configs[agent_name] - llm_config = copy.deepcopy(self.llm_config) - llm_config.stop = agent_config.stop - baseAgent: BaseAgent = getattr(self.agent_module, agent_config.role.agent_type) - base_agent = baseAgent( - role=agent_config.role, - prompt_config = agent_config.prompt_config, - prompt_manager_type=agent_config.prompt_manager_type, - task = task, - memory = memory, - chat_turn=agent_config.chat_turn, - focus_agents=agent_config.focus_agents, - focus_message_keys=agent_config.focus_message_keys, - llm_config=llm_config, - embed_config=self.embed_config, - sandbox_server=self.sandbox_server, - jupyter_work_path=self.jupyter_work_path, - kb_root_path=self.kb_root_path, - doc_retrieval=self.doc_retrieval, - code_retrieval=self.code_retrieval, - search_retrieval=self.search_retrieval, - log_verbose=self.log_verbose - ) - if agent_config.role.agent_type == "SelectorAgent": - for group_agent_name in agent_config.group_agents: - group_agent_config = role_configs[group_agent_name] - llm_config = copy.deepcopy(self.llm_config) - llm_config.stop = group_agent_config.stop - baseAgent: BaseAgent = getattr(self.agent_module, group_agent_config.role.agent_type) - group_base_agent = baseAgent( - role=group_agent_config.role, - prompt_config = group_agent_config.prompt_config, - prompt_manager_type=group_agent_config.prompt_manager_type, - task = task, - memory = memory, - chat_turn=group_agent_config.chat_turn, - focus_agents=group_agent_config.focus_agents, - focus_message_keys=group_agent_config.focus_message_keys, - llm_config=llm_config, - embed_config=self.embed_config, - sandbox_server=self.sandbox_server, - jupyter_work_path=self.jupyter_work_path, - kb_root_path=self.kb_root_path, - doc_retrieval=self.doc_retrieval, - code_retrieval=self.code_retrieval, - search_retrieval=self.search_retrieval, - log_verbose=self.log_verbose - ) - base_agent.group_agents.append(group_base_agent) - - agents.append(base_agent) - - chain_instance = BaseChain( - chain_config, - agents, - jupyter_work_path=self.jupyter_work_path, - sandbox_server=self.sandbox_server, - embed_config=self.embed_config, - llm_config=self.llm_config, - kb_root_path=self.kb_root_path, - doc_retrieval=self.doc_retrieval, - code_retrieval=self.code_retrieval, - search_retrieval=self.search_retrieval, - log_verbose=self.log_verbose - ) - chains.append(chain_instance) - - return chains - - def update(self) -> Memory: - pass - - def get_memory(self, ) -> Memory: - return Memory.from_memory_list( - [chain.get_memory() for chain in self.chains] - ) - - def get_memory_str(self, do_all_memory=True, content_key="role_content") -> str: - memory = self.global_memory if do_all_memory else self.phase_memory - return "\n".join([": ".join(i) for i in memory.to_tuple_messages(content_key=content_key)]) - - def get_chains_memory(self, content_key="role_content") -> List[Tuple]: - return [memory.to_tuple_messages(content_key=content_key) for memory in self.phase_memory] - - def get_chains_memory_str(self, content_key="role_content") -> str: - return "************".join([f"{chain.chainConfig.chain_name}\n" + chain.get_memory_str(content_key=content_key) for chain in self.chains]) \ No newline at end of file diff --git a/coagent/connector/prompt_manager.py b/coagent/connector/prompt_manager.py deleted file mode 100644 index 192b12b..0000000 --- a/coagent/connector/prompt_manager.py +++ /dev/null @@ -1,350 +0,0 @@ -from coagent.connector.schema import Memory, Message -import random -from textwrap import dedent -import copy -from loguru import logger - -from coagent.connector.utils import extract_section, parse_section - - -class PromptManager: - def __init__(self, role_prompt="", prompt_config=None, monitored_agents=[], monitored_fields=[]): - self.role_prompt = role_prompt - self.monitored_agents = monitored_agents - self.monitored_fields = monitored_fields - self.field_handlers = {} - self.context_handlers = {} - self.field_order = [] # 用于普通字段的顺序 - self.context_order = [] # 单独维护上下文字段的顺序 - self.field_descriptions = {} - self.omit_if_empty_flags = {} - self.context_title = "### Context Data\n\n" - - self.prompt_config = prompt_config - if self.prompt_config: - self.register_fields_from_config() - - def register_field(self, field_name, function=None, title=None, description=None, is_context=True, omit_if_empty=True): - """ - 注册一个新的字段及其处理函数。 - Args: - field_name (str): 字段名称。 - function (callable): 处理字段数据的函数。 - title (str, optional): 字段的自定义标题(可选)。 - description (str, optional): 字段的描述(可选,可以是几句话)。 - is_context (bool, optional): 指示该字段是否为上下文字段。 - omit_if_empty (bool, optional): 如果数据为空,是否省略该字段。 - """ - if not function: - function = self.handle_custom_data - - # Register the handler function based on context flag - if is_context: - self.context_handlers[field_name] = function - else: - self.field_handlers[field_name] = function - - # Store the custom title if provided and adjust the title prefix based on context - title_prefix = "####" if is_context else "###" - if title is not None: - self.field_descriptions[field_name] = f"{title_prefix} {title}\n\n" - elif description is not None: - # If title is not provided but description is, use description as title - self.field_descriptions[field_name] = f"{title_prefix} {field_name.replace('_', ' ').title()}\n\n{description}\n\n" - else: - # If neither title nor description is provided, use the field name as title - self.field_descriptions[field_name] = f"{title_prefix} {field_name.replace('_', ' ').title()}\n\n" - - # Store the omit_if_empty flag for this field - self.omit_if_empty_flags[field_name] = omit_if_empty - - if is_context and field_name != 'context_placeholder': - self.context_handlers[field_name] = function - self.context_order.append(field_name) - else: - self.field_handlers[field_name] = function - self.field_order.append(field_name) - - def generate_full_prompt(self, **kwargs): - full_prompt = [] - context_prompts = [] # 用于收集上下文内容 - is_pre_print = kwargs.get("is_pre_print", False) # 用于强制打印所有prompt 字段信息,不管有没有空 - - # 先处理上下文字段 - for field_name in self.context_order: - handler = self.context_handlers[field_name] - processed_prompt = handler(**kwargs) - # Check if the field should be omitted when empty - if self.omit_if_empty_flags.get(field_name, False) and not processed_prompt and not is_pre_print: - continue # Skip this field - title_or_description = self.field_descriptions.get(field_name, f"#### {field_name.replace('_', ' ').title()}\n\n") - context_prompts.append(title_or_description + processed_prompt + '\n\n') - - # 处理普通字段,同时查找 context_placeholder 的位置 - for field_name in self.field_order: - if field_name == 'context_placeholder': - # 在 context_placeholder 的位置插入上下文数据 - full_prompt.append(self.context_title) # 添加上下文部分的大标题 - full_prompt.extend(context_prompts) # 添加收集的上下文内容 - else: - handler = self.field_handlers[field_name] - processed_prompt = handler(**kwargs) - # Check if the field should be omitted when empty - if self.omit_if_empty_flags.get(field_name, False) and not processed_prompt and not is_pre_print: - continue # Skip this field - title_or_description = self.field_descriptions.get(field_name, f"### {field_name.replace('_', ' ').title()}\n\n") - full_prompt.append(title_or_description + processed_prompt + '\n\n') - - # 返回完整的提示,移除尾部的空行 - return ''.join(full_prompt).rstrip('\n') - - def pre_print(self, **kwargs): - kwargs.update({"is_pre_print": True}) - prompt = self.generate_full_prompt(**kwargs) - - input_keys = parse_section(self.role_prompt, 'Response Output Format') - llm_predict = "\n".join([f"**{k}:**" for k in input_keys]) - return prompt + "\n\n" + "#"*19 + "\n<<<>>>\n" + "#"*19 + f"\n\n{llm_predict}\n" - - def handle_custom_data(self, **kwargs): - return "" - - def handle_tool_data(self, **kwargs): - if 'previous_agent_message' not in kwargs: - return "" - - previous_agent_message = kwargs.get('previous_agent_message') - tools = previous_agent_message.tools - - if not tools: - return "" - - tool_strings = [] - for tool in tools: - args_schema = str(tool.args) - tool_strings.append(f"{tool.name}: {tool.description}, args: {args_schema}") - formatted_tools = "\n".join(tool_strings) - - tool_names = ", ".join([tool.name for tool in tools]) - - tool_prompt = dedent(f""" -Below is a list of tools that are available for your use: -{formatted_tools} - -valid "tool_name" value is: -{tool_names} -""") - - return tool_prompt - - def handle_agent_data(self, **kwargs): - if 'agents' not in kwargs: - return "" - - agents = kwargs.get('agents') - random.shuffle(agents) - agent_names = ", ".join([f'{agent.role.role_name}' for agent in agents]) - agent_descs = [] - for agent in agents: - role_desc = agent.role.role_prompt.split("####")[1] - while "\n\n" in role_desc: - role_desc = role_desc.replace("\n\n", "\n") - role_desc = role_desc.replace("\n", ",") - - agent_descs.append(f'"role name: {agent.role.role_name}\nrole description: {role_desc}"') - - agents = "\n".join(agent_descs) - agent_prompt = f''' - Please ensure your selection is one of the listed roles. Available roles for selection: - {agents} - Please ensure select the Role from agent names, such as {agent_names}''' - - return dedent(agent_prompt) - - def handle_doc_info(self, **kwargs) -> str: - if 'previous_agent_message' not in kwargs: - return "" - previous_agent_message: Message = kwargs.get('previous_agent_message') - db_docs = previous_agent_message.db_docs - search_docs = previous_agent_message.search_docs - code_cocs = previous_agent_message.code_docs - doc_infos = "\n".join([doc.get_snippet() for doc in db_docs] + [doc.get_snippet() for doc in search_docs] + - [doc.get_code() for doc in code_cocs]) - return doc_infos - - def handle_session_records(self, **kwargs) -> str: - - memory_pool: Memory = kwargs.get('memory_pool', Memory(messages=[])) - memory_pool = self.select_memory_by_agent_name(memory_pool) - memory_pool = self.select_memory_by_parsed_key(memory_pool) - - return memory_pool.to_str_messages(content_key="parsed_output_list", with_tag=True) - - def handle_current_plan(self, **kwargs) -> str: - if 'previous_agent_message' not in kwargs: - return "" - previous_agent_message = kwargs['previous_agent_message'] - return previous_agent_message.parsed_output.get("CURRENT_STEP", "") - - def handle_agent_profile(self, **kwargs) -> str: - return extract_section(self.role_prompt, 'Agent Profile') - - def handle_output_format(self, **kwargs) -> str: - return extract_section(self.role_prompt, 'Response Output Format') - - def handle_response(self, **kwargs) -> str: - if 'react_memory' not in kwargs: - return "" - - react_memory = kwargs.get('react_memory', Memory(messages=[])) - if react_memory is None: - return "" - - return "\n".join(["\n".join([f"**{k}:**\n{v}" for k,v in _dict.items()]) for _dict in react_memory.get_parserd_output()]) - - def handle_task_records(self, **kwargs) -> str: - if 'task_memory' not in kwargs: - return "" - - task_memory: Memory = kwargs.get('task_memory', Memory(messages=[])) - if task_memory is None: - return "" - - return "\n".join(["\n".join([f"**{k}:**\n{v}" for k,v in _dict.items() if k not in ["CURRENT_STEP"]]) for _dict in task_memory.get_parserd_output()]) - - def handle_previous_message(self, message: Message) -> str: - pass - - def handle_message_by_role_name(self, message: Message) -> str: - pass - - def handle_message_by_role_type(self, message: Message) -> str: - pass - - def handle_current_agent_react_message(self, message: Message) -> str: - pass - - def extract_codedoc_info_for_prompt(self, message: Message) -> str: - code_docs = message.code_docs - doc_infos = "\n".join([doc.get_code() for doc in code_docs]) - return doc_infos - - def select_memory_by_parsed_key(self, memory: Memory) -> Memory: - return Memory( - messages=[self.select_message_by_parsed_key(message) for message in memory.messages - if self.select_message_by_parsed_key(message) is not None] - ) - - def select_memory_by_agent_name(self, memory: Memory) -> Memory: - return Memory( - messages=[self.select_message_by_agent_name(message) for message in memory.messages - if self.select_message_by_agent_name(message) is not None] - ) - - def select_message_by_agent_name(self, message: Message) -> Message: - # assume we focus all agents - if self.monitored_agents == []: - return message - return None if message is None or message.role_name not in self.monitored_agents else self.select_message_by_parsed_key(message) - - def select_message_by_parsed_key(self, message: Message) -> Message: - # assume we focus all key contents - if message is None: - return message - - if self.monitored_fields == []: - return message - - message_c = copy.deepcopy(message) - message_c.parsed_output = {k: v for k,v in message_c.parsed_output.items() if k in self.monitored_fields} - message_c.parsed_output_list = [{k: v for k,v in parsed_output.items() if k in self.monitored_fields} for parsed_output in message_c.parsed_output_list] - return message_c - - def get_memory(self, content_key="role_content"): - return self.memory.to_tuple_messages(content_key="step_content") - - def get_memory_str(self, content_key="role_content"): - return "\n".join([": ".join(i) for i in self.memory.to_tuple_messages(content_key="step_content")]) - - def register_fields_from_config(self): - - for prompt_field in self.prompt_config: - - function_name = prompt_field.function_name - # 检查function_name是否是self的一个方法 - if function_name and hasattr(self, function_name): - function = getattr(self, function_name) - else: - function = self.handle_custom_data - - self.register_field(prompt_field.field_name, - function=function, - title=prompt_field.title, - description=prompt_field.description, - is_context=prompt_field.is_context, - omit_if_empty=prompt_field.omit_if_empty) - - def register_standard_fields(self): - self.register_field('agent_profile', function=self.handle_agent_profile, is_context=False) - self.register_field('tool_information', function=self.handle_tool_data, is_context=False) - self.register_field('context_placeholder', is_context=True) # 用于标记上下文数据部分的位置 - self.register_field('reference_documents', function=self.handle_doc_info, is_context=True) - self.register_field('session_records', function=self.handle_session_records, is_context=True) - self.register_field('output_format', function=self.handle_output_format, title='Response Output Format', is_context=False) - self.register_field('response', function=self.handle_response, is_context=False, omit_if_empty=False) - - def register_executor_fields(self): - self.register_field('agent_profile', function=self.handle_agent_profile, is_context=False) - self.register_field('tool_information', function=self.handle_tool_data, is_context=False) - self.register_field('context_placeholder', is_context=True) # 用于标记上下文数据部分的位置 - self.register_field('reference_documents', function=self.handle_doc_info, is_context=True) - self.register_field('session_records', function=self.handle_session_records, is_context=True) - self.register_field('current_plan', function=self.handle_current_plan, is_context=True) - self.register_field('output_format', function=self.handle_output_format, title='Response Output Format', is_context=False) - self.register_field('response', function=self.handle_response, is_context=False, omit_if_empty=False) - - def register_fields_from_dict(self, fields_dict): - # 使用字典注册字段的函数 - for field_name, field_config in fields_dict.items(): - function_name = field_config.get('function', None) - title = field_config.get('title', None) - description = field_config.get('description', None) - is_context = field_config.get('is_context', True) - omit_if_empty = field_config.get('omit_if_empty', True) - - # 检查function_name是否是self的一个方法 - if function_name and hasattr(self, function_name): - function = getattr(self, function_name) - else: - function = self.handle_custom_data - - # 调用已存在的register_field方法注册字段 - self.register_field(field_name, function=function, title=title, description=description, is_context=is_context, omit_if_empty=omit_if_empty) - - - -def main(): - manager = PromptManager() - manager.register_standard_fields() - - manager.register_field('agents_work_progress', title=f"Agents' Work Progress", is_context=True) - - # 创建数据字典 - data_dict = { - "agent_profile": "这是代理配置文件...", - # "tool_list": "这是工具列表...", - "reference_documents": "这是参考文档...", - "session_records": "这是会话记录...", - "agents_work_progress": "这是代理工作进展...", - "output_format": "这是预期的输出格式...", - # "response": "这是生成或继续回应的指令...", - "response": "", - "test": 'xxxxx' - } - - # 组合完整的提示 - full_prompt = manager.generate_full_prompt(data_dict) - print(full_prompt) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/coagent/connector/prompt_manager/__init__.py b/coagent/connector/prompt_manager/__init__.py deleted file mode 100644 index 8957e25..0000000 --- a/coagent/connector/prompt_manager/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .prompt_manager import PromptManager -from .extend_manager import * \ No newline at end of file diff --git a/coagent/connector/prompt_manager/extend_manager.py b/coagent/connector/prompt_manager/extend_manager.py deleted file mode 100644 index 69cc449..0000000 --- a/coagent/connector/prompt_manager/extend_manager.py +++ /dev/null @@ -1,45 +0,0 @@ - -from coagent.connector.schema import Message -from .prompt_manager import PromptManager - - -class Code2DocPM(PromptManager): - def handle_code_snippet(self, **kwargs) -> str: - if 'previous_agent_message' not in kwargs: - return "" - previous_agent_message: Message = kwargs['previous_agent_message'] - code_snippet = previous_agent_message.customed_kargs.get("Code Snippet", "") - current_vertex = previous_agent_message.customed_kargs.get("Current_Vertex", "") - instruction = "A segment of code that contains the function or method to be documented.\n" - return instruction + "\n" + f"name: {current_vertex}\n{code_snippet}" - - def handle_specific_objective(self, **kwargs) -> str: - if 'previous_agent_message' not in kwargs: - return "" - previous_agent_message: Message = kwargs['previous_agent_message'] - specific_objective = previous_agent_message.parsed_output.get("Code Path") - - instruction = "Provide the code path of the function or method you wish to document.\n" - s = instruction + f"\n{specific_objective}" - return s - - -class CodeRetrievalPM(PromptManager): - def handle_code_snippet(self, **kwargs) -> str: - if 'previous_agent_message' not in kwargs: - return "" - previous_agent_message: Message = kwargs['previous_agent_message'] - code_snippet = previous_agent_message.customed_kargs.get("Code Snippet", "") - current_vertex = previous_agent_message.customed_kargs.get("Current_Vertex", "") - instruction = "the initial Code or objective that the user wanted to achieve" - return instruction + "\n" + f"name: {current_vertex}\n{code_snippet}" - - def handle_retrieval_codes(self, **kwargs) -> str: - if 'previous_agent_message' not in kwargs: - return "" - previous_agent_message: Message = kwargs['previous_agent_message'] - Retrieval_Codes = previous_agent_message.customed_kargs["Retrieval_Codes"] - Relative_vertex = previous_agent_message.customed_kargs["Relative_vertex"] - instruction = "the initial Code or objective that the user wanted to achieve" - s = instruction + "\n" + "\n".join([f"name: {vertext}\n{code}" for vertext, code in zip(Relative_vertex, Retrieval_Codes)]) - return s diff --git a/coagent/connector/prompt_manager/prompt_manager.py b/coagent/connector/prompt_manager/prompt_manager.py deleted file mode 100644 index 57d454a..0000000 --- a/coagent/connector/prompt_manager/prompt_manager.py +++ /dev/null @@ -1,353 +0,0 @@ -import random -from textwrap import dedent -import copy -from loguru import logger - -from langchain.agents.tools import Tool - -from coagent.connector.schema import Memory, Message -from coagent.connector.utils import extract_section, parse_section - - - -class PromptManager: - def __init__(self, role_prompt="", prompt_config=None, monitored_agents=[], monitored_fields=[]): - self.role_prompt = role_prompt - self.monitored_agents = monitored_agents - self.monitored_fields = monitored_fields - self.field_handlers = {} - self.context_handlers = {} - self.field_order = [] # 用于普通字段的顺序 - self.context_order = [] # 单独维护上下文字段的顺序 - self.field_descriptions = {} - self.omit_if_empty_flags = {} - self.context_title = "### Context Data\n\n" - - self.prompt_config = prompt_config - if self.prompt_config: - self.register_fields_from_config() - - def register_field(self, field_name, function=None, title=None, description=None, is_context=True, omit_if_empty=True): - """ - 注册一个新的字段及其处理函数。 - Args: - field_name (str): 字段名称。 - function (callable): 处理字段数据的函数。 - title (str, optional): 字段的自定义标题(可选)。 - description (str, optional): 字段的描述(可选,可以是几句话)。 - is_context (bool, optional): 指示该字段是否为上下文字段。 - omit_if_empty (bool, optional): 如果数据为空,是否省略该字段。 - """ - if not function: - function = self.handle_custom_data - - # Register the handler function based on context flag - if is_context: - self.context_handlers[field_name] = function - else: - self.field_handlers[field_name] = function - - # Store the custom title if provided and adjust the title prefix based on context - title_prefix = "####" if is_context else "###" - if title is not None: - self.field_descriptions[field_name] = f"{title_prefix} {title}\n\n" - elif description is not None: - # If title is not provided but description is, use description as title - self.field_descriptions[field_name] = f"{title_prefix} {field_name.replace('_', ' ').title()}\n\n{description}\n\n" - else: - # If neither title nor description is provided, use the field name as title - self.field_descriptions[field_name] = f"{title_prefix} {field_name.replace('_', ' ').title()}\n\n" - - # Store the omit_if_empty flag for this field - self.omit_if_empty_flags[field_name] = omit_if_empty - - if is_context and field_name != 'context_placeholder': - self.context_handlers[field_name] = function - self.context_order.append(field_name) - else: - self.field_handlers[field_name] = function - self.field_order.append(field_name) - - def generate_full_prompt(self, **kwargs): - full_prompt = [] - context_prompts = [] # 用于收集上下文内容 - is_pre_print = kwargs.get("is_pre_print", False) # 用于强制打印所有prompt 字段信息,不管有没有空 - - # 先处理上下文字段 - for field_name in self.context_order: - handler = self.context_handlers[field_name] - processed_prompt = handler(**kwargs) - # Check if the field should be omitted when empty - if self.omit_if_empty_flags.get(field_name, False) and not processed_prompt and not is_pre_print: - continue # Skip this field - title_or_description = self.field_descriptions.get(field_name, f"#### {field_name.replace('_', ' ').title()}\n\n") - context_prompts.append(title_or_description + processed_prompt + '\n\n') - - # 处理普通字段,同时查找 context_placeholder 的位置 - for field_name in self.field_order: - if field_name == 'context_placeholder': - # 在 context_placeholder 的位置插入上下文数据 - full_prompt.append(self.context_title) # 添加上下文部分的大标题 - full_prompt.extend(context_prompts) # 添加收集的上下文内容 - else: - handler = self.field_handlers[field_name] - processed_prompt = handler(**kwargs) - # Check if the field should be omitted when empty - if self.omit_if_empty_flags.get(field_name, False) and not processed_prompt and not is_pre_print: - continue # Skip this field - title_or_description = self.field_descriptions.get(field_name, f"### {field_name.replace('_', ' ').title()}\n\n") - full_prompt.append(title_or_description + processed_prompt + '\n\n') - - # 返回完整的提示,移除尾部的空行 - return ''.join(full_prompt).rstrip('\n') - - def pre_print(self, **kwargs): - kwargs.update({"is_pre_print": True}) - prompt = self.generate_full_prompt(**kwargs) - - input_keys = parse_section(self.role_prompt, 'Response Output Format') - llm_predict = "\n".join([f"**{k}:**" for k in input_keys]) - return prompt + "\n\n" + "#"*19 + "\n<<<>>>\n" + "#"*19 + f"\n\n{llm_predict}\n" - - def handle_custom_data(self, **kwargs): - return "" - - def handle_tool_data(self, **kwargs): - if 'previous_agent_message' not in kwargs: - return "" - - previous_agent_message = kwargs.get('previous_agent_message') - tools: list[Tool] = previous_agent_message.tools - - if not tools: - return "" - - tool_strings = [] - for tool in tools: - args_str = f'args: {str(tool.args)}' if tool.args_schema else "" - tool_strings.append(f"{tool.name}: {tool.description}, {args_str}") - formatted_tools = "\n".join(tool_strings) - - tool_names = ", ".join([tool.name for tool in tools]) - - tool_prompt = dedent(f""" -Below is a list of tools that are available for your use: -{formatted_tools} - -valid "tool_name" value is: -{tool_names} -""") - - return tool_prompt - - def handle_agent_data(self, **kwargs): - if 'agents' not in kwargs: - return "" - - agents = kwargs.get('agents') - random.shuffle(agents) - agent_names = ", ".join([f'{agent.role.role_name}' for agent in agents]) - agent_descs = [] - for agent in agents: - role_desc = agent.role.role_prompt.split("####")[1] - while "\n\n" in role_desc: - role_desc = role_desc.replace("\n\n", "\n") - role_desc = role_desc.replace("\n", ",") - - agent_descs.append(f'"role name: {agent.role.role_name}\nrole description: {role_desc}"') - - agents = "\n".join(agent_descs) - agent_prompt = f''' - Please ensure your selection is one of the listed roles. Available roles for selection: - {agents} - Please ensure select the Role from agent names, such as {agent_names}''' - - return dedent(agent_prompt) - - def handle_doc_info(self, **kwargs) -> str: - if 'previous_agent_message' not in kwargs: - return "" - previous_agent_message: Message = kwargs.get('previous_agent_message') - db_docs = previous_agent_message.db_docs - search_docs = previous_agent_message.search_docs - code_cocs = previous_agent_message.code_docs - doc_infos = "\n".join([doc.get_snippet() for doc in db_docs] + [doc.get_snippet() for doc in search_docs] + - [doc.get_code() for doc in code_cocs]) - return doc_infos - - def handle_session_records(self, **kwargs) -> str: - - memory_pool: Memory = kwargs.get('memory_pool', Memory(messages=[])) - memory_pool = self.select_memory_by_agent_name(memory_pool) - memory_pool = self.select_memory_by_parsed_key(memory_pool) - - return memory_pool.to_str_messages(content_key="parsed_output_list", with_tag=True) - - def handle_current_plan(self, **kwargs) -> str: - if 'previous_agent_message' not in kwargs: - return "" - previous_agent_message = kwargs['previous_agent_message'] - return previous_agent_message.parsed_output.get("CURRENT_STEP", "") - - def handle_agent_profile(self, **kwargs) -> str: - return extract_section(self.role_prompt, 'Agent Profile') - - def handle_output_format(self, **kwargs) -> str: - return extract_section(self.role_prompt, 'Response Output Format') - - def handle_response(self, **kwargs) -> str: - if 'react_memory' not in kwargs: - return "" - - react_memory = kwargs.get('react_memory', Memory(messages=[])) - if react_memory is None: - return "" - - return "\n".join(["\n".join([f"**{k}:**\n{v}" for k,v in _dict.items()]) for _dict in react_memory.get_parserd_output()]) - - def handle_task_records(self, **kwargs) -> str: - if 'task_memory' not in kwargs: - return "" - - task_memory: Memory = kwargs.get('task_memory', Memory(messages=[])) - if task_memory is None: - return "" - - return "\n".join(["\n".join([f"**{k}:**\n{v}" for k,v in _dict.items() if k not in ["CURRENT_STEP"]]) for _dict in task_memory.get_parserd_output()]) - - def handle_previous_message(self, message: Message) -> str: - pass - - def handle_message_by_role_name(self, message: Message) -> str: - pass - - def handle_message_by_role_type(self, message: Message) -> str: - pass - - def handle_current_agent_react_message(self, message: Message) -> str: - pass - - def extract_codedoc_info_for_prompt(self, message: Message) -> str: - code_docs = message.code_docs - doc_infos = "\n".join([doc.get_code() for doc in code_docs]) - return doc_infos - - def select_memory_by_parsed_key(self, memory: Memory) -> Memory: - return Memory( - messages=[self.select_message_by_parsed_key(message) for message in memory.messages - if self.select_message_by_parsed_key(message) is not None] - ) - - def select_memory_by_agent_name(self, memory: Memory) -> Memory: - return Memory( - messages=[self.select_message_by_agent_name(message) for message in memory.messages - if self.select_message_by_agent_name(message) is not None] - ) - - def select_message_by_agent_name(self, message: Message) -> Message: - # assume we focus all agents - if self.monitored_agents == []: - return message - return None if message is None or message.role_name not in self.monitored_agents else self.select_message_by_parsed_key(message) - - def select_message_by_parsed_key(self, message: Message) -> Message: - # assume we focus all key contents - if message is None: - return message - - if self.monitored_fields == []: - return message - - message_c = copy.deepcopy(message) - message_c.parsed_output = {k: v for k,v in message_c.parsed_output.items() if k in self.monitored_fields} - message_c.parsed_output_list = [{k: v for k,v in parsed_output.items() if k in self.monitored_fields} for parsed_output in message_c.parsed_output_list] - return message_c - - def get_memory(self, content_key="role_content"): - return self.memory.to_tuple_messages(content_key="step_content") - - def get_memory_str(self, content_key="role_content"): - return "\n".join([": ".join(i) for i in self.memory.to_tuple_messages(content_key="step_content")]) - - def register_fields_from_config(self): - - for prompt_field in self.prompt_config: - - function_name = prompt_field.function_name - # 检查function_name是否是self的一个方法 - if function_name and hasattr(self, function_name): - function = getattr(self, function_name) - else: - function = self.handle_custom_data - - self.register_field(prompt_field.field_name, - function=function, - title=prompt_field.title, - description=prompt_field.description, - is_context=prompt_field.is_context, - omit_if_empty=prompt_field.omit_if_empty) - - def register_standard_fields(self): - self.register_field('agent_profile', function=self.handle_agent_profile, is_context=False) - self.register_field('tool_information', function=self.handle_tool_data, is_context=False) - self.register_field('context_placeholder', is_context=True) # 用于标记上下文数据部分的位置 - self.register_field('reference_documents', function=self.handle_doc_info, is_context=True) - self.register_field('session_records', function=self.handle_session_records, is_context=True) - self.register_field('output_format', function=self.handle_output_format, title='Response Output Format', is_context=False) - self.register_field('response', function=self.handle_response, is_context=False, omit_if_empty=False) - - def register_executor_fields(self): - self.register_field('agent_profile', function=self.handle_agent_profile, is_context=False) - self.register_field('tool_information', function=self.handle_tool_data, is_context=False) - self.register_field('context_placeholder', is_context=True) # 用于标记上下文数据部分的位置 - self.register_field('reference_documents', function=self.handle_doc_info, is_context=True) - self.register_field('session_records', function=self.handle_session_records, is_context=True) - self.register_field('current_plan', function=self.handle_current_plan, is_context=True) - self.register_field('output_format', function=self.handle_output_format, title='Response Output Format', is_context=False) - self.register_field('response', function=self.handle_response, is_context=False, omit_if_empty=False) - - def register_fields_from_dict(self, fields_dict): - # 使用字典注册字段的函数 - for field_name, field_config in fields_dict.items(): - function_name = field_config.get('function', None) - title = field_config.get('title', None) - description = field_config.get('description', None) - is_context = field_config.get('is_context', True) - omit_if_empty = field_config.get('omit_if_empty', True) - - # 检查function_name是否是self的一个方法 - if function_name and hasattr(self, function_name): - function = getattr(self, function_name) - else: - function = self.handle_custom_data - - # 调用已存在的register_field方法注册字段 - self.register_field(field_name, function=function, title=title, description=description, is_context=is_context, omit_if_empty=omit_if_empty) - - - -def main(): - manager = PromptManager() - manager.register_standard_fields() - - manager.register_field('agents_work_progress', title=f"Agents' Work Progress", is_context=True) - - # 创建数据字典 - data_dict = { - "agent_profile": "这是代理配置文件...", - # "tool_list": "这是工具列表...", - "reference_documents": "这是参考文档...", - "session_records": "这是会话记录...", - "agents_work_progress": "这是代理工作进展...", - "output_format": "这是预期的输出格式...", - # "response": "这是生成或继续回应的指令...", - "response": "", - "test": 'xxxxx' - } - - # 组合完整的提示 - full_prompt = manager.generate_full_prompt(data_dict) - print(full_prompt) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/coagent/connector/schema/__init__.py b/coagent/connector/schema/__init__.py deleted file mode 100644 index 8f90bd7..0000000 --- a/coagent/connector/schema/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from .memory import Memory -from .general_schema import * -from .message import Message - -__all__ = [ - "Memory", "ActionStatus", "Doc", "CodeDoc", "Task", "LogVerboseEnum", - "Env", "Role", "ChainConfig", "AgentConfig", "PhaseConfig", "Message", - "load_role_configs", "load_chain_configs", "load_phase_configs" -] \ No newline at end of file diff --git a/coagent/connector/schema/general_schema.py b/coagent/connector/schema/general_schema.py deleted file mode 100644 index c45aa5e..0000000 --- a/coagent/connector/schema/general_schema.py +++ /dev/null @@ -1,309 +0,0 @@ -from pydantic import BaseModel -from typing import List, Dict, Optional, Union -from enum import Enum -import re -import json -from loguru import logger -from langchain.tools import BaseTool - - -class ActionStatus(Enum): - DEFAUILT = "default" - - FINISHED = "finished" - STOPPED = "stopped" - CONTINUED = "continued" - - TOOL_USING = "tool_using" - CODING = "coding" - CODE_EXECUTING = "code_executing" - CODING2FILE = "coding2file" - - PLANNING = "planning" - UNCHANGED = "unchanged" - ADJUSTED = "adjusted" - CODE_RETRIEVAL = "code_retrieval" - - def __eq__(self, other): - if isinstance(other, str): - return self.value.lower() == other.lower() - return super().__eq__(other) - - -class Action(BaseModel): - action_name: str - description: str - -class FinishedAction(Action): - action_name: str = ActionStatus.FINISHED - description: str = "provide the final answer to the original query to break the chain answer" - -class StoppedAction(Action): - action_name: str = ActionStatus.STOPPED - description: str = "provide the final answer to the original query to break the agent answer" - -class ContinuedAction(Action): - action_name: str = ActionStatus.CONTINUED - description: str = "cant't provide the final answer to the original query" - -class ToolUsingAction(Action): - action_name: str = ActionStatus.TOOL_USING - description: str = "proceed with using the specified tool." - -class CodingdAction(Action): - action_name: str = ActionStatus.CODING - description: str = "provide the answer by writing code" - -class Coding2FileAction(Action): - action_name: str = ActionStatus.CODING2FILE - description: str = "provide the answer by writing code and filename" - -class CodeExecutingAction(Action): - action_name: str = ActionStatus.CODE_EXECUTING - description: str = "provide the answer by writing executable code" - -class PlanningAction(Action): - action_name: str = ActionStatus.PLANNING - description: str = "provide a sequence of tasks" - -class UnchangedAction(Action): - action_name: str = ActionStatus.UNCHANGED - description: str = "this PLAN has no problem, just set PLAN_STEP to CURRENT_STEP+1." - -class AdjustedAction(Action): - action_name: str = ActionStatus.ADJUSTED - description: str = "the PLAN is to provide an optimized version of the original plan." - -# extended action exmaple -class CodeRetrievalAction(Action): - action_name: str = ActionStatus.CODE_RETRIEVAL - description: str = "execute the code retrieval to acquire more code information" - - -class RoleTypeEnums(Enum): - SYSTEM = "system" - USER = "user" - ASSISTANT = "assistant" - FUNCTION = "function" - OBSERVATION = "observation" - SUMMARY = "summary" - - def __eq__(self, other): - if isinstance(other, str): - return self.value == other - return super().__eq__(other) - - -class PromptKey(BaseModel): - key_name: str - description: str - -class PromptKeyEnums(Enum): - # Origin Query is ui's user question - ORIGIN_QUERY = "origin_query" - # agent's input from last agent - CURRENT_QUESTION = "current_question" - # ui memory contaisn (user and assistants) - UI_MEMORY = "ui_memory" - # agent's memory - SELF_MEMORY = "self_memory" - # chain memory - CHAIN_MEMORY = "chain_memory" - # agent's memory - SELF_LOCAL_MEMORY = "self_local_memory" - # chain memory - CHAIN_LOCAL_MEMORY = "chain_local_memory" - # Doc Infomations contains (Doc\Code\Search) - DOC_INFOS = "doc_infos" - - def __eq__(self, other): - if isinstance(other, str): - return self.value == other - return super().__eq__(other) - - -class Doc(BaseModel): - title: str - snippet: str - link: str - index: int - - def get_title(self): - return self.title - - def get_snippet(self, ): - return self.snippet - - def get_link(self, ): - return self.link - - def get_index(self, ): - return self.index - - def to_json(self): - return vars(self) - - def __str__(self,): - return f"""出处 [{self.index + 1}] 标题 [{self.title}]\n\n来源 ({self.link}) \n\n内容 {self.snippet}\n\n""" - - -class CodeDoc(BaseModel): - code: str - related_nodes: list - index: int - - def get_code(self, ): - return self.code - - def get_related_node(self, ): - return self.related_nodes - - def get_index(self, ): - return self.index - - def to_json(self): - return vars(self) - - def __str__(self,): - return f"""出处 [{self.index + 1}] \n\n来源 ({self.related_nodes}) \n\n内容 {self.code}\n\n""" - - -class LogVerboseEnum(Enum): - Log0Level = "0" # don't print log - Log1Level = "1" # print level-1 log - Log2Level = "2" # print level-2 log - Log3Level = "3" # print level-3 log - - def __eq__(self, other): - if isinstance(other, str): - return self.value.lower() == other.lower() - if isinstance(other, LogVerboseEnum): - return self.value == other.value - return False - - def __ge__(self, other): - if isinstance(other, LogVerboseEnum): - return int(self.value) >= int(other.value) - if isinstance(other, str): - return int(self.value) >= int(other) - return NotImplemented - - def __le__(self, other): - if isinstance(other, LogVerboseEnum): - return int(self.value) <= int(other.value) - if isinstance(other, str): - return int(self.value) <= int(other) - return NotImplemented - - @classmethod - def ge(self, enum_value: 'LogVerboseEnum', other: Union[str, 'LogVerboseEnum']): - return enum_value <= other - - -class Task(BaseModel): - task_type: str - task_name: str - task_desc: str - task_prompt: str - -class Env(BaseModel): - env_type: str - env_name: str - env_desc:str - - -class Role(BaseModel): - role_type: str - role_name: str - role_desc: str = "" - agent_type: str = "BaseAgent" - role_prompt: str = "" - template_prompt: str = "" - - -class ChainConfig(BaseModel): - chain_name: str - chain_type: str = "BaseChain" - agents: List[str] - do_checker: bool = False - chat_turn: int = 1 - - -class PromptField(BaseModel): - field_name: str # 假设这是一个函数类型,您可以根据需要更改 - function_name: str - title: Optional[str] = None - description: Optional[str] = None - is_context: Optional[bool] = True - omit_if_empty: Optional[bool] = True - - -class AgentConfig(BaseModel): - role: Role - prompt_config: List[PromptField] - prompt_manager_type: str = "PromptManager" - chat_turn: int = 1 - focus_agents: List = [] - focus_message_keys: List = [] - group_agents: List = [] - stop: str = "" - - -class PhaseConfig(BaseModel): - phase_name: str - phase_type: str - chains: List[str] - do_summary: bool = False - do_search: bool = False - do_doc_retrieval: bool = False - do_code_retrieval: bool = False - do_tool_retrieval: bool = False - - -class CompleteChainConfig(BaseModel): - chain_name: str - chain_type: str - agents: Dict[str, AgentConfig] - do_checker: bool = False - chat_turn: int = 1 - - -class CompletePhaseConfig(BaseModel): - phase_name: str - phase_type: str - chains: Dict[str, CompleteChainConfig] - do_summary: bool = False - do_search: bool = False - do_doc_retrieval: bool = False - do_code_retrieval: bool = False - do_tool_retrieval: bool = False - - -def load_role_configs(config) -> Dict[str, AgentConfig]: - if isinstance(config, str): - with open(config, 'r', encoding="utf8") as file: - configs = json.load(file) - else: - configs = config - # logger.debug(configs) - return {name: AgentConfig(**v) for name, v in configs.items()} - - -def load_chain_configs(config) -> Dict[str, ChainConfig]: - if isinstance(config, str): - with open(config, 'r', encoding="utf8") as file: - configs = json.load(file) - else: - configs = config - return {name: ChainConfig(**v) for name, v in configs.items()} - - -def load_phase_configs(config) -> Dict[str, PhaseConfig]: - if isinstance(config, str): - with open(config, 'r', encoding="utf8") as file: - configs = json.load(file) - else: - configs = config - return {name: PhaseConfig(**v) for name, v in configs.items()} - -# AgentConfig.update_forward_refs() \ No newline at end of file diff --git a/coagent/connector/schema/memory.py b/coagent/connector/schema/memory.py deleted file mode 100644 index 07b92fd..0000000 --- a/coagent/connector/schema/memory.py +++ /dev/null @@ -1,161 +0,0 @@ -from pydantic import BaseModel -from typing import List, Union, Dict -from loguru import logger - -from .message import Message -from coagent.utils.common_utils import ( - save_to_jsonl_file, save_to_json_file, read_json_file, read_jsonl_file -) - - -class Memory(BaseModel): - messages: List[Message] = [] - - # def __init__(self, messages: List[Message] = []): - # self.messages = messages - - def append(self, message: Message): - self.messages.append(message) - - def extend(self, memory: 'Memory'): - self.messages.extend(memory.messages) - - def update(self, role_name: str, role_type: str, role_content: str): - self.messages.append(Message(role_name, role_type, role_content, role_content)) - - def clear(self, ): - self.messages = [] - - def delete(self, ): - pass - - def get_messages(self, k=0) -> List[Message]: - """Return the most recent k memories, return all when k=0""" - return self.messages[-k:] - - def split_by_role_type(self) -> List[Dict[str, 'Memory']]: - """ - Split messages into rounds of conversation based on role_type. - Each round consists of consecutive messages of the same role_type. - User messages form a single round, while assistant and function messages are combined into a single round. - Each round is represented by a dict with 'role' and 'memory' keys, with assistant and function messages - labeled as 'assistant'. - """ - rounds = [] - current_memory = Memory() - current_role = None - - logger.debug(len(self.messages)) - - for msg in self.messages: - # Determine the message's role, considering 'function' as 'assistant' - message_role = 'assistant' if msg.role_type in ['assistant', 'function'] else 'user' - - # If the current memory is empty or the current message is of the same role_type as current_role, add to current memory - if not current_memory.messages or current_role == message_role: - current_memory.append(msg) - else: - # Finish the current memory and start a new one - rounds.append({'role': current_role, 'memory': current_memory}) - current_memory = Memory() - current_memory.append(msg) - - # Update the current_role, considering 'function' as 'assistant' - current_role = message_role - - # Don't forget to add the last memory if it exists - if current_memory.messages: - rounds.append({'role': current_role, 'memory': current_memory}) - - logger.debug(rounds) - - return rounds - - def format_rounds_to_html(self) -> str: - formatted_html_str = "" - rounds = self.split_by_role_type() - - for round in rounds: - role = round['role'] - memory = round['memory'] - - # 转换当前round的Memory为字符串 - messages_str = memory.to_str_messages() - - # 根据角色类型添加相应的HTML标签 - if role == 'user': - formatted_html_str += f"\n{messages_str}\n\n" - else: # 对于'assistant'和'function'角色,我们将其视为'assistant' - formatted_html_str += f"\n{messages_str}\n\n" - - return formatted_html_str - - - def filter_by_role_type(self, role_types: List[str]) -> List[Message]: - # Filter messages based on role types - return [message for message in self.messages if message.role_type not in role_types] - - def select_by_role_type(self, role_types: List[str]) -> List[Message]: - # Select messages based on role types - return [message for message in self.messages if message.role_type in role_types] - - def to_tuple_messages(self, return_all: bool = True, content_key="role_content", filter_roles=[]): - # Convert messages to tuples based on parameters - # logger.debug(f"{[message.to_tuple_message(return_all, content_key) for message in self.messages ]}") - return [ - message.to_tuple_message(return_all, content_key) for message in self.messages - if message.role_name not in filter_roles - ] - - def to_dict_messages(self, filter_roles=[]): - # Convert messages to dictionaries based on filter roles - return [ - message.to_dict_message() for message in self.messages - if message.role_name not in filter_roles - ] - - def to_str_messages(self, return_all: bool = True, content_key="role_content", filter_roles=[], with_tag=False): - # Convert messages to strings based on parameters - # for message in self.messages: - # logger.debug(f"{message.role_name}: {message.to_str_content(return_all, content_key, with_tag=with_tag)}") - # logger.debug(f"{[message.to_tuple_message(return_all, content_key) for message in self.messages ]}") - return "\n\n".join([message.to_str_content(return_all, content_key, with_tag=with_tag) for message in self.messages - if message.role_name not in filter_roles - ]) - - def get_parserd_output(self, ): - return [message.parsed_output for message in self.messages] - - def get_parserd_output_list(self, ): - # for message in self.messages: - # logger.debug(f"{message.role_name}: {message.parsed_output_list}") - # return [parsed_output for message in self.messages for parsed_output in message.parsed_output_list[1:]] - return [parsed_output for message in self.messages for parsed_output in message.parsed_output_list] - - def get_spec_parserd_output(self, ): - return [message.spec_parsed_output for message in self.messages] - - def get_rolenames(self, ): - '''''' - return [message.role_name for message in self.messages] - - @classmethod - def from_memory_list(cls, memorys: List['Memory']) -> 'Memory': - return cls(messages=[message for memory in memorys for message in memory.get_messages()]) - - def __len__(self, ): - return len(self.messages) - - def __str__(self) -> str: - return "\n".join([":".join(i) for i in self.to_tuple_messages()]) - - def __add__(self, other: Union[Message, 'Memory']) -> 'Memory': - if isinstance(other, Message): - return Memory(messages=self.messages + [other]) - elif isinstance(other, Memory): - return Memory(messages=self.messages + other.messages) - else: - raise ValueError(f"cant add unspecified type like as {type(other)}") - - - \ No newline at end of file diff --git a/coagent/connector/schema/message.py b/coagent/connector/schema/message.py deleted file mode 100644 index 92f9658..0000000 --- a/coagent/connector/schema/message.py +++ /dev/null @@ -1,121 +0,0 @@ -from pydantic import BaseModel, root_validator -from loguru import logger - -from coagent.utils.common_utils import getCurrentDatetime -from .general_schema import * - - -class Message(BaseModel): - chat_index: str = None - user_name: str = "default" - role_name: str - role_type: str - role_prompt: str = None - input_query: str = None - origin_query: str = None - datetime: str = getCurrentDatetime() - - # llm output - role_content: str = None - step_content: str = None - - # llm parsed information - plans: List[str] = None - code_content: str = None - code_filename: str = None - tool_params: str = None - tool_name: str = None - parsed_output: dict = {} - spec_parsed_output: dict = {} - parsed_output_list: List[Dict] = [] - - # llm\tool\code executre information - action_status: str = "default" - agent_index: int = None - code_answer: str = None - tool_answer: str = None - observation: str = None - figures: Dict[str, str] = {} - - # prompt support information - tools: List[BaseTool] = [] - task: Task = None - db_docs: List['Doc'] = [] - code_docs: List['CodeDoc'] = [] - search_docs: List['Doc'] = [] - agents: List = [] - - # phase input - phase_name: str = None - chain_name: str = None - do_search: bool = False - doc_engine_name: str = None - code_engine_name: str = None - cb_search_type: str = None - search_engine_name: str = None - top_k: int = 3 - use_nh: bool = True - local_graph_path: str = '' - score_threshold: float = 1.0 - do_doc_retrieval: bool = False - do_code_retrieval: bool = False - do_tool_retrieval: bool = False - history_node_list: List[str] = [] - # user's customed kargs for init or end action - customed_kargs: dict = {} - - - @root_validator(pre=True) - def check_card_number_omitted(cls, values): - input_query = values.get("input_query") - origin_query = values.get("origin_query") - role_content = values.get("role_content") - if input_query is None: - values["input_query"] = origin_query or role_content - if role_content is None: - values["role_content"] = origin_query - return values - - # pydantic>=2.0 - # @model_validator(mode='after') - # def check_passwords_match(self) -> 'Message': - # if self.input_query is None: - # self.input_query = self.origin_query or self.role_content - # if self.role_content is None: - # self.role_content = self.origin_query - # return self - - def to_tuple_message(self, return_all: bool = True, content_key="role_content"): - role_content = self.to_str_content(False, content_key) - if return_all: - return (self.role_name, role_content) - else: - return (role_content) - - def to_dict_message(self, ): - return vars(self) - - def to_str_content(self, return_all: bool = True, content_key="role_content", with_tag=False): - if content_key == "role_content": - role_content = self.role_content or self.input_query - elif content_key == "step_content": - role_content = self.step_content or self.role_content or self.input_query - elif content_key == "parsed_output": - role_content = "\n".join([f"**{k}:** {v}" for k, v in self.parsed_output.items()]) - elif content_key == "parsed_output_list": - role_content = "\n".join([f"**{k}:** {v}" for po in self.parsed_output_list for k,v in po.items()]) - else: - role_content = self.role_content or self.input_query - - if with_tag: - start_tag = f"<{self.role_type}-{self.role_name}-message>" - end_tag = f"" - return f"{start_tag}\n{role_content}\n{end_tag}" - else: - return role_content - - def __str__(self) -> str: - # key_str = '\n'.join([k for k, v in vars(self).items()]) - # logger.debug(f"{key_str}") - return "\n".join([": ".join([k, str(v)]) for k, v in vars(self).items()]) - \ No newline at end of file diff --git a/coagent/connector/utils.py b/coagent/connector/utils.py deleted file mode 100644 index f3f8bfb..0000000 --- a/coagent/connector/utils.py +++ /dev/null @@ -1,122 +0,0 @@ -import re, copy, json -from loguru import logger - - -def extract_section(text, section_name): - # Define a pattern to extract the named section along with its content - section_pattern = rf'#### {section_name}\n(.*?)(?=####|$)' - - # Find the specific section content - section_content = re.search(section_pattern, text, re.DOTALL) - - if section_content: - # If the section is found, extract the content and strip the leading/trailing whitespace - # This will also remove leading/trailing newlines - content = section_content.group(1).strip() - - # Return the cleaned content - return content - else: - # If the section is not found, return an empty string - return "" - - -def parse_section(text, section_name): - # Define a pattern to extract the named section along with its content - section_pattern = rf'#### {section_name}\n(.*?)(?=####|$)' - - # Find the specific section content - section_content = re.search(section_pattern, text, re.DOTALL) - - if section_content: - # If the section is found, extract the content - content = section_content.group(1) - - # Define a pattern to find segments that follow the format **xx:** - segments_pattern = r'\*\*([^*]+):\*\*' - - # Use findall method to extract all matches in the section content - segments = re.findall(segments_pattern, content) - - return segments - else: - # If the section is not found, return an empty list - return [] - - -def parse_text_to_dict(text): - # Define a regular expression pattern to capture the key and value - main_pattern = r"\*\*(.+?):\*\*\s*(.*?)\s*(?=\*\*|$)" - list_pattern = r'```python\n(.*?)```' - plan_pattern = r'\[\s*.*?\s*\]' - - # Use re.findall to find all main matches in the text - main_matches = re.findall(main_pattern, text, re.DOTALL) - - # Convert main matches to a dictionary - parsed_dict = {key.strip(): value.strip() for key, value in main_matches} - - for k, v in parsed_dict.items(): - for pattern in [list_pattern, plan_pattern]: - if "PLAN" != k: continue - v = v.replace("```list", "```python") - match_value = re.search(pattern, v, re.DOTALL) - if match_value: - # Add the code block to the dictionary - parsed_dict[k] = eval(match_value.group(1).strip()) - break - - return parsed_dict - - -def parse_dict_to_dict(parsed_dict) -> dict: - code_pattern = r'```python\n(.*?)```' - tool_pattern = r'```json\n(.*?)```' - java_pattern = r'```java\n(.*?)```' - - pattern_dict = {"code": code_pattern, "json": tool_pattern, "java": java_pattern} - spec_parsed_dict = copy.deepcopy(parsed_dict) - for key, pattern in pattern_dict.items(): - for k, text in parsed_dict.items(): - # Search for the code block - if not isinstance(text, str): - spec_parsed_dict[k] = text - continue - _match = re.search(pattern, text, re.DOTALL) - if _match: - # Add the code block to the dictionary - try: - spec_parsed_dict[key] = json.loads(_match.group(1).strip()) - spec_parsed_dict[k] = json.loads(_match.group(1).strip()) - except: - spec_parsed_dict[key] = _match.group(1).strip() - spec_parsed_dict[k] = _match.group(1).strip() - break - return spec_parsed_dict - - -def prompt_cost(model_type: str, num_prompt_tokens: float, num_completion_tokens: float): - input_cost_map = { - "gpt-3.5-turbo": 0.0015, - "gpt-3.5-turbo-16k": 0.003, - "gpt-3.5-turbo-0613": 0.0015, - "gpt-3.5-turbo-16k-0613": 0.003, - "gpt-4": 0.03, - "gpt-4-0613": 0.03, - "gpt-4-32k": 0.06, - } - - output_cost_map = { - "gpt-3.5-turbo": 0.002, - "gpt-3.5-turbo-16k": 0.004, - "gpt-3.5-turbo-0613": 0.002, - "gpt-3.5-turbo-16k-0613": 0.004, - "gpt-4": 0.06, - "gpt-4-0613": 0.06, - "gpt-4-32k": 0.12, - } - - if model_type not in input_cost_map or model_type not in output_cost_map: - return -1 - - return num_prompt_tokens * input_cost_map[model_type] / 1000.0 + num_completion_tokens * output_cost_map[model_type] / 1000.0 diff --git a/coagent/db_handler/__init__.py b/coagent/db_handler/__init__.py deleted file mode 100644 index 83c9d92..0000000 --- a/coagent/db_handler/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: __init__.py.py -@time: 2023/11/16 下午3:15 -@desc: -''' \ No newline at end of file diff --git a/coagent/db_handler/graph_db_handler/__init__.py b/coagent/db_handler/graph_db_handler/__init__.py deleted file mode 100644 index 6d3396a..0000000 --- a/coagent/db_handler/graph_db_handler/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: __init__.py.py -@time: 2023/11/20 下午3:07 -@desc: -''' \ No newline at end of file diff --git a/coagent/db_handler/graph_db_handler/nebula_handler.py b/coagent/db_handler/graph_db_handler/nebula_handler.py deleted file mode 100644 index 062b4e7..0000000 --- a/coagent/db_handler/graph_db_handler/nebula_handler.py +++ /dev/null @@ -1,285 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: nebula_handler.py -@time: 2023/11/16 下午3:15 -@desc: -''' -import time -from loguru import logger - -from nebula3.gclient.net import ConnectionPool -from nebula3.Config import Config - - -class NebulaHandler: - def __init__(self, host: str, port: int, username: str, password: str = '', space_name: str = ''): - ''' - init nebula connection_pool - @param host: host - @param port: port - @param username: username - @param password: password - ''' - config = Config() - - self.connection_pool = ConnectionPool() - self.connection_pool.init([(host, port)], config) - self.username = username - self.password = password - self.space_name = space_name - - def execute_cypher(self, cypher: str, space_name: str = '', format_res: bool = False, use_space_name: bool = True): - ''' - - @param space_name: space_name, if provided, will execute use space_name first - @param cypher: - @return: - ''' - with self.connection_pool.session_context(self.username, self.password) as session: - if use_space_name: - if space_name: - cypher = f'USE {space_name};{cypher}' - elif self.space_name: - cypher = f'USE {self.space_name};{cypher}' - - # logger.debug(cypher) - resp = session.execute(cypher) - - if format_res: - resp = self.result_to_dict(resp) - return resp - - def close_connection(self): - self.connection_pool.close() - - def create_space(self, space_name: str, vid_type: str, comment: str = ''): - ''' - create space - @param space_name: cannot startwith number - @return: - ''' - cypher = f'CREATE SPACE IF NOT EXISTS {space_name} (vid_type={vid_type}) comment="{comment}";' - resp = self.execute_cypher(cypher, use_space_name=False) - - return resp - - def show_space(self): - cypher = 'SHOW SPACES' - resp = self.execute_cypher(cypher) - return resp - - def drop_space(self, space_name): - cypher = f'DROP SPACE {space_name}' - return self.execute_cypher(cypher) - - def create_tag(self, tag_name: str, prop_dict: dict = {}): - ''' - 创建 tag - @param tag_name: tag 名称 - @param prop_dict: 属性字典 {'prop 名字': 'prop 类型'} - @return: - ''' - cypher = f'CREATE TAG IF NOT EXISTS {tag_name}' - cypher += '(' - for k, v in prop_dict.items(): - cypher += f'{k} {v},' - cypher = cypher.rstrip(',') - cypher += ')' - cypher += ';' - - res = self.execute_cypher(cypher, self.space_name) - return res - - def show_tags(self): - ''' - 查看 tag - @return: - ''' - cypher = 'SHOW TAGS' - resp = self.execute_cypher(cypher, self.space_name) - return resp - - def insert_vertex(self, tag_name: str, value_dict: dict): - ''' - insert vertex - @param tag_name: - @param value_dict: {'properties_name': [], values: {'vid':[]}} order should be the same in properties_name and values - @return: - ''' - cypher = f'INSERT VERTEX {tag_name} (' - - properties_name = value_dict['properties_name'] - - for property_name in properties_name: - cypher += f'{property_name},' - cypher = cypher.rstrip(',') - - cypher += ') VALUES ' - - for vid, properties in value_dict['values'].items(): - cypher += f'"{vid}":(' - for property in properties: - if type(property) == str: - cypher += f'"{property}",' - else: - cypher += f'{property}' - cypher = cypher.rstrip(',') - cypher += '),' - cypher = cypher.rstrip(',') - cypher += ';' - - res = self.execute_cypher(cypher, self.space_name) - return res - - def create_edge_type(self, edge_type_name: str, prop_dict: dict = {}): - ''' - 创建 tag - @param edge_type_name: tag 名称 - @param prop_dict: 属性字典 {'prop 名字': 'prop 类型'} - @return: - ''' - cypher = f'CREATE EDGE IF NOT EXISTS {edge_type_name}' - - cypher += '(' - for k, v in prop_dict.items(): - cypher += f'{k} {v},' - cypher = cypher.rstrip(',') - cypher += ')' - cypher += ';' - - res = self.execute_cypher(cypher, self.space_name) - return res - - def show_edge_type(self): - ''' - 查看 tag - @return: - ''' - cypher = 'SHOW EDGES' - resp = self.execute_cypher(cypher, self.space_name) - return resp - - def drop_edge_type(self, edge_type_name: str): - cypher = f'DROP EDGE {edge_type_name}' - return self.execute_cypher(cypher, self.space_name) - - def insert_edge(self, edge_type_name: str, value_dict: dict): - ''' - insert edge - @param edge_type_name: - @param value_dict: value_dict: {'properties_name': [], values: {(src_vid, dst_vid):[]}} order should be the - same in properties_name and values - @return: - ''' - cypher = f'INSERT EDGE {edge_type_name} (' - - properties_name = value_dict['properties_name'] - - for property_name in properties_name: - cypher += f'{property_name},' - cypher = cypher.rstrip(',') - - cypher += ') VALUES ' - - for (src_vid, dst_vid), properties in value_dict['values'].items(): - cypher += f'"{src_vid}"->"{dst_vid}":(' - for property in properties: - if type(property) == str: - cypher += f'"{property}",' - else: - cypher += f'{property}' - cypher = cypher.rstrip(',') - cypher += '),' - cypher = cypher.rstrip(',') - cypher += ';' - - res = self.execute_cypher(cypher, self.space_name) - return res - - def set_space_name(self, space_name): - self.space_name = space_name - - def add_host(self, host: str, port: str): - ''' - add host - @return: - ''' - cypher = f'ADD HOSTS {host}:{port}' - res = self.execute_cypher(cypher) - return res - - def get_stat(self): - ''' - - @return: - ''' - submit_cypher = 'SUBMIT JOB STATS;' - self.execute_cypher(cypher=submit_cypher, space_name=self.space_name) - time.sleep(2) - - stats_cypher = 'SHOW STATS;' - stats_res = self.execute_cypher(cypher=stats_cypher, space_name=self.space_name) - - res = {'vertices': -1, 'edges': -1} - - stats_res_dict = self.result_to_dict(stats_res) - logger.info(stats_res_dict) - for idx in range(len(stats_res_dict['Type'])): - t = stats_res_dict['Type'][idx].as_string() - name = stats_res_dict['Name'][idx].as_string() - count = stats_res_dict['Count'][idx].as_int() - - if t == 'Space' and name in res: - res[name] = count - return res - - def get_vertices(self, tag_name: str = '', limit: int = 10000): - ''' - get all vertices - @return: - ''' - if tag_name: - cypher = f'''MATCH (v:{tag_name}) RETURN v LIMIT {limit};''' - else: - cypher = f'MATCH (v) RETURN v LIMIT {limit};' - - res = self.execute_cypher(cypher, self.space_name) - return self.result_to_dict(res) - - def get_all_vertices(self,): - ''' - get all vertices - @return: - ''' - cypher = "MATCH (v) RETURN v;" - res = self.execute_cypher(cypher, self.space_name) - return self.result_to_dict(res) - - def get_relative_vertices(self, vertice): - ''' - get all vertices - @return: - ''' - cypher = f'''MATCH (v1)--(v2) WHERE id(v1) == '{vertice}' RETURN id(v2) as id;''' - res = self.execute_cypher(cypher, self.space_name) - return self.result_to_dict(res) - - def result_to_dict(self, result) -> dict: - """ - build list for each column, and transform to dataframe - """ - # logger.info(result.error_msg()) - assert result.is_succeeded() - columns = result.keys() - d = {} - for col_num in range(result.col_size()): - col_name = columns[col_num] - col_list = result.column_values(col_name) - d[col_name] = [x for x in col_list] - return d - - - - - diff --git a/coagent/db_handler/vector_db_handler/__init__.py b/coagent/db_handler/vector_db_handler/__init__.py deleted file mode 100644 index 2b67ecf..0000000 --- a/coagent/db_handler/vector_db_handler/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: __init__.py.py -@time: 2023/11/20 下午3:08 -@desc: -''' \ No newline at end of file diff --git a/coagent/db_handler/vector_db_handler/chroma_handler.py b/coagent/db_handler/vector_db_handler/chroma_handler.py deleted file mode 100644 index f86141c..0000000 --- a/coagent/db_handler/vector_db_handler/chroma_handler.py +++ /dev/null @@ -1,144 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: chroma_handler.py -@time: 2023/11/21 下午12:21 -@desc: -''' -from loguru import logger -import chromadb - - -class ChromaHandler: - def __init__(self, path: str, collection_name: str = ''): - ''' - init client - @param path: path of data - @collection_name: name of collection - ''' - settings = chromadb.get_settings() - # disable the posthog telemetry mechnism that may raise the connection error, such as - # "requests.exceptions.ConnectTimeout: HTTPSConnectionPool(host='us-api.i.posthog.com', port 443)" - settings.anonymized_telemetry = False - self.client = chromadb.PersistentClient(path, settings) - self.client.heartbeat() - - if collection_name: - self.collection = self.client.get_or_create_collection(name=collection_name) - - def create_collection(self, collection_name: str): - ''' - create collection, if exists, will override - @return: - ''' - try: - collection = self.client.create_collection(name=collection_name) - except Exception as e: - return {'result_code': -1, 'msg': f'fail, error={e}'} - return {'result_code': 0, 'msg': 'success'} - - def delete_collection(self, collection_name: str): - ''' - - @param collection_name: - @return: - ''' - try: - self.client.delete_collection(name=collection_name) - except Exception as e: - return {'result_code': -1, 'msg': f'fail, error={e}'} - return {'result_code': 0, 'msg': 'success'} - - def set_collection(self, collection_name: str): - ''' - - @param collection_name: - @return: - ''' - try: - self.collection = self.client.get_collection(collection_name) - except Exception as e: - return {'result_code': -1, 'msg': f'fail, error={e}'} - return {'result_code': 0, 'msg': 'success'} - - def add_data(self, ids: list, documents: list = None, embeddings: list = None, metadatas: list = None): - ''' - add data to chroma - @param documents: list of doc string - @param embeddings: list of vector - @param metadatas: list of metadata - @param ids: list of id - @return: - ''' - try: - self.collection.add( - ids=ids, - embeddings=embeddings, - metadatas=metadatas, - documents=documents - ) - except Exception as e: - return {'result_code': -1, 'msg': f'fail, error={e}'} - return {'result_code': 0, 'msg': 'success'} - - def query(self, query_embeddings=None, query_texts=None, n_results=10, where=None, where_document=None, - include=["metadatas", "documents", "distances"]): - ''' - - @param query_embeddings: - @param query_texts: - @param n_results: - @param where: - @param where_document: - @param include: - @return: - ''' - try: - query_result = self.collection.query(query_embeddings=query_embeddings, query_texts=query_texts, - n_results=n_results, where=where, where_document=where_document, - include=include) - return {'result_code': 0, 'msg': 'success', 'result': query_result} - except Exception as e: - return {'result_code': -1, 'msg': f'fail, error={e}'} - - def get(self, ids=None, where=None, limit=None, offset=None, where_document=None, include=["metadatas", "documents"]): - ''' - get by condition - @param ids: - @param where: - @param limit: - @param offset: - @param where_document: - @param include: - @return: - ''' - try: - query_result = self.collection.get(ids=ids, where=where, where_document=where_document, - limit=limit, - offset=offset, include=include) - return {'result_code': 0, 'msg': 'success', 'result': query_result} - except Exception as e: - return {'result_code': -1, 'msg': f'fail, error={e}'} - - def peek(self, limit: int=10): - ''' - peek - @param limit: - @return: - ''' - try: - query_result = self.collection.peek(limit) - return {'result_code': 0, 'msg': 'success', 'result': query_result} - except Exception as e: - return {'result_code': -1, 'msg': f'fail, error={e}'} - - def count(self): - ''' - count - @return: - ''' - try: - query_result = self.collection.count() - return {'result_code': 0, 'msg': 'success', 'result': query_result} - except Exception as e: - return {'result_code': -1, 'msg': f'fail, error={e}'} diff --git a/coagent/document_loaders/__init__.py b/coagent/document_loaders/__init__.py deleted file mode 100644 index 2343aeb..0000000 --- a/coagent/document_loaders/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .json_loader import JSONLoader -from .jsonl_loader import JSONLLoader - -__all__ = [ - "JSONLoader", "JSONLLoader" -] \ No newline at end of file diff --git a/coagent/document_loaders/json_loader.py b/coagent/document_loaders/json_loader.py deleted file mode 100644 index 4e5ecd3..0000000 --- a/coagent/document_loaders/json_loader.py +++ /dev/null @@ -1,61 +0,0 @@ -import json -from pathlib import Path -from typing import AnyStr, Callable, Dict, List, Optional, Union - -from langchain.docstore.document import Document -from langchain.document_loaders.base import BaseLoader -from langchain.text_splitter import RecursiveCharacterTextSplitter, TextSplitter - -from coagent.utils.common_utils import read_json_file - - -class JSONLoader(BaseLoader): - - def __init__( - self, - file_path: Union[str, Path], - schema_key: str = "all_text", - content_key: Optional[str] = None, - metadata_func: Optional[Callable[[Dict, Dict], Dict]] = None, - text_content: bool = True, - ): - self.file_path = Path(file_path).resolve() - self.schema_key = schema_key - self._content_key = content_key - self._metadata_func = metadata_func - self._text_content = text_content - - def load(self, ) -> List[Document]: - """Load and return documents from the JSON file.""" - docs: List[Document] = [] - datas = read_json_file(self.file_path) - self._parse(datas, docs) - return docs - - def _parse(self, datas: List, docs: List[Document]) -> None: - for idx, sample in enumerate(datas): - metadata = dict( - source=str(self.file_path), - seq_num=idx, - ) - text = sample.get(self.schema_key, "") - docs.append(Document(page_content=text, metadata=metadata)) - - def load_and_split( - self, text_splitter: Optional[TextSplitter] = None - ) -> List[Document]: - """Load Documents and split into chunks. Chunks are returned as Documents. - - Args: - text_splitter: TextSplitter instance to use for splitting documents. - Defaults to RecursiveCharacterTextSplitter. - - Returns: - List of Documents. - """ - if text_splitter is None: - _text_splitter: TextSplitter = RecursiveCharacterTextSplitter() - else: - _text_splitter = text_splitter - docs = self.load() - return _text_splitter.split_documents(docs) \ No newline at end of file diff --git a/coagent/document_loaders/jsonl_loader.py b/coagent/document_loaders/jsonl_loader.py deleted file mode 100644 index a56e6eb..0000000 --- a/coagent/document_loaders/jsonl_loader.py +++ /dev/null @@ -1,62 +0,0 @@ -import json -from pathlib import Path -from typing import AnyStr, Callable, Dict, List, Optional, Union - -from langchain.docstore.document import Document -from langchain.document_loaders.base import BaseLoader -from langchain.text_splitter import RecursiveCharacterTextSplitter, TextSplitter - -from coagent.utils.common_utils import read_jsonl_file - - -class JSONLLoader(BaseLoader): - - def __init__( - self, - file_path: Union[str, Path], - schema_key: str = "all_text", - content_key: Optional[str] = None, - metadata_func: Optional[Callable[[Dict, Dict], Dict]] = None, - text_content: bool = True, - ): - self.file_path = Path(file_path).resolve() - self.schema_key = schema_key - self._content_key = content_key - self._metadata_func = metadata_func - self._text_content = text_content - - def load(self, ) -> List[Document]: - """Load and return documents from the JSON file.""" - docs: List[Document] = [] - datas = read_jsonl_file(self.file_path) - self._parse(datas, docs) - return docs - - def _parse(self, datas: List, docs: List[Document]) -> None: - for idx, sample in enumerate(datas): - metadata = dict( - source=str(self.file_path), - seq_num=idx, - ) - text = sample.get(self.schema_key, "") - docs.append(Document(page_content=text, metadata=metadata)) - - def load_and_split( - self, text_splitter: Optional[TextSplitter] = None - ) -> List[Document]: - """Load Documents and split into chunks. Chunks are returned as Documents. - - Args: - text_splitter: TextSplitter instance to use for splitting documents. - Defaults to RecursiveCharacterTextSplitter. - - Returns: - List of Documents. - """ - if text_splitter is None: - _text_splitter: TextSplitter = RecursiveCharacterTextSplitter() - else: - _text_splitter = text_splitter - - docs = self.load() - return _text_splitter.split_documents(docs) \ No newline at end of file diff --git a/coagent/embeddings/__init__.py b/coagent/embeddings/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/coagent/embeddings/commands/__init__.py b/coagent/embeddings/commands/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/coagent/embeddings/commands/default_vs_cds.py b/coagent/embeddings/commands/default_vs_cds.py deleted file mode 100644 index 94707cb..0000000 --- a/coagent/embeddings/commands/default_vs_cds.py +++ /dev/null @@ -1,37 +0,0 @@ -from typing import List - -from langchain.embeddings.base import Embeddings -from langchain.schema import Document - - - -class BaseVSCService: - def do_create_kb(self): - pass - - def do_drop_kb(self): - pass - - def do_add_doc(self, docs: List[Document], embeddings: Embeddings): - pass - - def do_clear_vs(self): - pass - - def vs_type(self) -> str: - return "default" - - def do_init(self): - pass - - def do_search(self): - pass - - def do_insert_multi_knowledge(self): - pass - - def do_insert_one_knowledge(self): - pass - - def do_delete_doc(self): - pass diff --git a/coagent/embeddings/faiss_m.py b/coagent/embeddings/faiss_m.py deleted file mode 100644 index 31d124c..0000000 --- a/coagent/embeddings/faiss_m.py +++ /dev/null @@ -1,791 +0,0 @@ -"""Wrapper around FAISS vector database.""" -from __future__ import annotations - -import operator -import os -import pickle -import uuid -import warnings -from enum import Enum -from pathlib import Path -from typing import ( - Any, - Callable, - Dict, - Iterable, - List, - Optional, - Sized, - Tuple, -) - -import numpy as np - -from langchain.docstore.base import AddableMixin, Docstore -from langchain.docstore.document import Document -# from langchain.docstore.in_memory import InMemoryDocstore -from .in_memory import InMemoryDocstore -from langchain.embeddings.base import Embeddings -from langchain.vectorstores.base import VectorStore -from langchain.vectorstores.utils import maximal_marginal_relevance - - -class DistanceStrategy(str, Enum): - """Enumerator of the Distance strategies for calculating distances - between vectors.""" - - EUCLIDEAN_DISTANCE = "EUCLIDEAN_DISTANCE" - MAX_INNER_PRODUCT = "MAX_INNER_PRODUCT" - DOT_PRODUCT = "DOT_PRODUCT" - JACCARD = "JACCARD" - COSINE = "COSINE" - - -def dependable_faiss_import(no_avx2: Optional[bool] = None) -> Any: - """ - Import faiss if available, otherwise raise error. - If FAISS_NO_AVX2 environment variable is set, it will be considered - to load FAISS with no AVX2 optimization. - - Args: - no_avx2: Load FAISS strictly with no AVX2 optimization - so that the vectorstore is portable and compatible with other devices. - """ - if no_avx2 is None and "FAISS_NO_AVX2" in os.environ: - no_avx2 = bool(os.getenv("FAISS_NO_AVX2")) - - try: - if no_avx2: - from faiss import swigfaiss as faiss - else: - import faiss - except ImportError: - raise ImportError( - "Could not import faiss python package. " - "Please install it with `pip install faiss-gpu` (for CUDA supported GPU) " - "or `pip install faiss-cpu` (depending on Python version)." - ) - return faiss - - -def _len_check_if_sized(x: Any, y: Any, x_name: str, y_name: str) -> None: - if isinstance(x, Sized) and isinstance(y, Sized) and len(x) != len(y): - raise ValueError( - f"{x_name} and {y_name} expected to be equal length but " - f"len({x_name})={len(x)} and len({y_name})={len(y)}" - ) - return - - -class FAISS(VectorStore): - """Wrapper around FAISS vector database. - - To use, you must have the ``faiss`` python package installed. - - Example: - .. code-block:: python - - from langchain.embeddings.openai import OpenAIEmbeddings - from langchain.vectorstores import FAISS - - embeddings = OpenAIEmbeddings() - texts = ["FAISS is an important library", "LangChain supports FAISS"] - faiss = FAISS.from_texts(texts, embeddings) - - """ - - def __init__( - self, - embedding_function: Callable, - index: Any, - docstore: Docstore, - index_to_docstore_id: Dict[int, str], - relevance_score_fn: Optional[Callable[[float], float]] = None, - normalize_L2: bool = False, - distance_strategy: DistanceStrategy = DistanceStrategy.EUCLIDEAN_DISTANCE, - ): - """Initialize with necessary components.""" - self.embedding_function = embedding_function - self.index = index - self.docstore = docstore - self.index_to_docstore_id = index_to_docstore_id - self.distance_strategy = distance_strategy - self.override_relevance_score_fn = relevance_score_fn - self._normalize_L2 = normalize_L2 - if ( - self.distance_strategy != DistanceStrategy.EUCLIDEAN_DISTANCE - and self._normalize_L2 - ): - warnings.warn( - "Normalizing L2 is not applicable for metric type: {strategy}".format( - strategy=self.distance_strategy - ) - ) - - def __add( - self, - texts: Iterable[str], - embeddings: Iterable[List[float]], - metadatas: Optional[Iterable[dict]] = None, - ids: Optional[List[str]] = None, - ) -> List[str]: - faiss = dependable_faiss_import() - - if not isinstance(self.docstore, AddableMixin): - raise ValueError( - "If trying to add texts, the underlying docstore should support " - f"adding items, which {self.docstore} does not" - ) - - _len_check_if_sized(texts, metadatas, "texts", "metadatas") - _metadatas = metadatas or ({} for _ in texts) - documents = [ - Document(page_content=t, metadata=m) for t, m in zip(texts, _metadatas) - ] - - _len_check_if_sized(documents, embeddings, "documents", "embeddings") - _len_check_if_sized(documents, ids, "documents", "ids") - - # Add to the index. - vector = np.array(embeddings, dtype=np.float32) - if self._normalize_L2: - faiss.normalize_L2(vector) - self.index.add(vector) - - # Add information to docstore and index. - ids = ids or [str(uuid.uuid4()) for _ in texts] - self.docstore.add({id_: doc for id_, doc in zip(ids, documents)}) - starting_len = len(self.index_to_docstore_id) - index_to_id = {starting_len + j: id_ for j, id_ in enumerate(ids)} - self.index_to_docstore_id.update(index_to_id) - return ids - - def add_texts( - self, - texts: Iterable[str], - metadatas: Optional[List[dict]] = None, - ids: Optional[List[str]] = None, - **kwargs: Any, - ) -> List[str]: - """Run more texts through the embeddings and add to the vectorstore. - - Args: - texts: Iterable of strings to add to the vectorstore. - metadatas: Optional list of metadatas associated with the texts. - ids: Optional list of unique IDs. - - Returns: - List of ids from adding the texts into the vectorstore. - """ - # embeddings = [self.embedding_function(text) for text in texts] - embeddings = self.embedding_function(texts) - return self.__add(texts, embeddings, metadatas=metadatas, ids=ids) - - def add_embeddings( - self, - text_embeddings: Iterable[Tuple[str, List[float]]], - metadatas: Optional[List[dict]] = None, - ids: Optional[List[str]] = None, - **kwargs: Any, - ) -> List[str]: - """Run more texts through the embeddings and add to the vectorstore. - - Args: - text_embeddings: Iterable pairs of string and embedding to - add to the vectorstore. - metadatas: Optional list of metadatas associated with the texts. - ids: Optional list of unique IDs. - - Returns: - List of ids from adding the texts into the vectorstore. - """ - # Embed and create the documents. - texts, embeddings = zip(*text_embeddings) - return self.__add(texts, embeddings, metadatas=metadatas, ids=ids) - - def similarity_search_with_score_by_vector( - self, - embedding: List[float], - k: int = 4, - filter: Optional[Dict[str, Any]] = None, - fetch_k: int = 20, - **kwargs: Any, - ) -> List[Tuple[Document, float]]: - """Return docs most similar to query. - - Args: - embedding: Embedding vector to look up documents similar to. - k: Number of Documents to return. Defaults to 4. - filter (Optional[Dict[str, Any]]): Filter by metadata. Defaults to None. - fetch_k: (Optional[int]) Number of Documents to fetch before filtering. - Defaults to 20. - **kwargs: kwargs to be passed to similarity search. Can include: - score_threshold: Optional, a floating point value between 0 to 1 to - filter the resulting set of retrieved docs - - Returns: - List of documents most similar to the query text and L2 distance - in float for each. Lower score represents more similarity. - """ - faiss = dependable_faiss_import() - vector = np.array([embedding], dtype=np.float32) - if self._normalize_L2: - faiss.normalize_L2(vector) - scores, indices = self.index.search(vector, k if filter is None else fetch_k) - # 经过normalize的结果会超出1 - if self._normalize_L2: - scores = np.array([row / np.linalg.norm(row) if np.max(row) > 1 else row for row in scores]) - docs = [] - for j, i in enumerate(indices[0]): - if i == -1: - # This happens when not enough docs are returned. - continue - _id = self.index_to_docstore_id[i] - doc = self.docstore.search(_id) - if not isinstance(doc, Document): - raise ValueError(f"Could not find document for id {_id}, got {doc}") - if filter is not None: - filter = { - key: [value] if not isinstance(value, list) else value - for key, value in filter.items() - } - if all(doc.metadata.get(key) in value for key, value in filter.items()): - docs.append((doc, scores[0][j])) - else: - docs.append((doc, scores[0][j])) - - score_threshold = kwargs.get("score_threshold") - if score_threshold is not None: - cmp = ( - operator.ge - if self.distance_strategy - in (DistanceStrategy.MAX_INNER_PRODUCT, DistanceStrategy.JACCARD) - else operator.le - ) - docs = [ - (doc, similarity) - for doc, similarity in docs - if cmp(similarity, score_threshold) - ] - return docs[:k] - - def similarity_search_with_score( - self, - query: str, - k: int = 4, - filter: Optional[Dict[str, Any]] = None, - fetch_k: int = 20, - **kwargs: Any, - ) -> List[Tuple[Document, float]]: - """Return docs most similar to query. - - Args: - query: Text to look up documents similar to. - k: Number of Documents to return. Defaults to 4. - filter (Optional[Dict[str, str]]): Filter by metadata. Defaults to None. - fetch_k: (Optional[int]) Number of Documents to fetch before filtering. - Defaults to 20. - - Returns: - List of documents most similar to the query text with - L2 distance in float. Lower score represents more similarity. - """ - embedding = self.embedding_function(query) - docs = self.similarity_search_with_score_by_vector( - embedding, - k, - filter=filter, - fetch_k=fetch_k, - **kwargs, - ) - return docs - - def similarity_search_by_vector( - self, - embedding: List[float], - k: int = 4, - filter: Optional[Dict[str, Any]] = None, - fetch_k: int = 20, - **kwargs: Any, - ) -> List[Document]: - """Return docs most similar to embedding vector. - - Args: - embedding: Embedding to look up documents similar to. - k: Number of Documents to return. Defaults to 4. - filter (Optional[Dict[str, str]]): Filter by metadata. Defaults to None. - fetch_k: (Optional[int]) Number of Documents to fetch before filtering. - Defaults to 20. - - Returns: - List of Documents most similar to the embedding. - """ - docs_and_scores = self.similarity_search_with_score_by_vector( - embedding, - k, - filter=filter, - fetch_k=fetch_k, - **kwargs, - ) - return [doc for doc, _ in docs_and_scores] - - def similarity_search( - self, - query: str, - k: int = 4, - filter: Optional[Dict[str, Any]] = None, - fetch_k: int = 20, - **kwargs: Any, - ) -> List[Document]: - """Return docs most similar to query. - - Args: - query: Text to look up documents similar to. - k: Number of Documents to return. Defaults to 4. - filter: (Optional[Dict[str, str]]): Filter by metadata. Defaults to None. - fetch_k: (Optional[int]) Number of Documents to fetch before filtering. - Defaults to 20. - - Returns: - List of Documents most similar to the query. - """ - docs_and_scores = self.similarity_search_with_score( - query, k, filter=filter, fetch_k=fetch_k, **kwargs - ) - return [doc for doc, _ in docs_and_scores] - - def max_marginal_relevance_search_with_score_by_vector( - self, - embedding: List[float], - *, - k: int = 4, - fetch_k: int = 20, - lambda_mult: float = 0.5, - filter: Optional[Dict[str, Any]] = None, - ) -> List[Tuple[Document, float]]: - """Return docs and their similarity scores selected using the maximal marginal - relevance. - - Maximal marginal relevance optimizes for similarity to query AND diversity - among selected documents. - - Args: - embedding: Embedding to look up documents similar to. - k: Number of Documents to return. Defaults to 4. - fetch_k: Number of Documents to fetch before filtering to - pass to MMR algorithm. - lambda_mult: Number between 0 and 1 that determines the degree - of diversity among the results with 0 corresponding - to maximum diversity and 1 to minimum diversity. - Defaults to 0.5. - Returns: - List of Documents and similarity scores selected by maximal marginal - relevance and score for each. - """ - scores, indices = self.index.search( - np.array([embedding], dtype=np.float32), - fetch_k if filter is None else fetch_k * 2, - ) - if filter is not None: - filtered_indices = [] - for i in indices[0]: - if i == -1: - # This happens when not enough docs are returned. - continue - _id = self.index_to_docstore_id[i] - doc = self.docstore.search(_id) - if not isinstance(doc, Document): - raise ValueError(f"Could not find document for id {_id}, got {doc}") - if all( - doc.metadata.get(key) in value - if isinstance(value, list) - else doc.metadata.get(key) == value - for key, value in filter.items() - ): - filtered_indices.append(i) - indices = np.array([filtered_indices]) - # -1 happens when not enough docs are returned. - embeddings = [self.index.reconstruct(int(i)) for i in indices[0] if i != -1] - mmr_selected = maximal_marginal_relevance( - np.array([embedding], dtype=np.float32), - embeddings, - k=k, - lambda_mult=lambda_mult, - ) - selected_indices = [indices[0][i] for i in mmr_selected] - selected_scores = [scores[0][i] for i in mmr_selected] - docs_and_scores = [] - for i, score in zip(selected_indices, selected_scores): - if i == -1: - # This happens when not enough docs are returned. - continue - _id = self.index_to_docstore_id[i] - doc = self.docstore.search(_id) - if not isinstance(doc, Document): - raise ValueError(f"Could not find document for id {_id}, got {doc}") - docs_and_scores.append((doc, score)) - return docs_and_scores - - def max_marginal_relevance_search_by_vector( - self, - embedding: List[float], - k: int = 4, - fetch_k: int = 20, - lambda_mult: float = 0.5, - filter: Optional[Dict[str, Any]] = None, - **kwargs: Any, - ) -> List[Document]: - """Return docs selected using the maximal marginal relevance. - - Maximal marginal relevance optimizes for similarity to query AND diversity - among selected documents. - - Args: - embedding: Embedding to look up documents similar to. - k: Number of Documents to return. Defaults to 4. - fetch_k: Number of Documents to fetch before filtering to - pass to MMR algorithm. - lambda_mult: Number between 0 and 1 that determines the degree - of diversity among the results with 0 corresponding - to maximum diversity and 1 to minimum diversity. - Defaults to 0.5. - Returns: - List of Documents selected by maximal marginal relevance. - """ - docs_and_scores = self.max_marginal_relevance_search_with_score_by_vector( - embedding, k=k, fetch_k=fetch_k, lambda_mult=lambda_mult, filter=filter - ) - return [doc for doc, _ in docs_and_scores] - - def max_marginal_relevance_search( - self, - query: str, - k: int = 4, - fetch_k: int = 20, - lambda_mult: float = 0.5, - filter: Optional[Dict[str, Any]] = None, - **kwargs: Any, - ) -> List[Document]: - """Return docs selected using the maximal marginal relevance. - - Maximal marginal relevance optimizes for similarity to query AND diversity - among selected documents. - - Args: - query: Text to look up documents similar to. - k: Number of Documents to return. Defaults to 4. - fetch_k: Number of Documents to fetch before filtering (if needed) to - pass to MMR algorithm. - lambda_mult: Number between 0 and 1 that determines the degree - of diversity among the results with 0 corresponding - to maximum diversity and 1 to minimum diversity. - Defaults to 0.5. - Returns: - List of Documents selected by maximal marginal relevance. - """ - embedding = self.embedding_function(query) - docs = self.max_marginal_relevance_search_by_vector( - embedding, - k=k, - fetch_k=fetch_k, - lambda_mult=lambda_mult, - filter=filter, - **kwargs, - ) - return docs - - def delete(self, ids: Optional[List[str]] = None, **kwargs: Any) -> Optional[bool]: - """Delete by ID. These are the IDs in the vectorstore. - - Args: - ids: List of ids to delete. - - Returns: - Optional[bool]: True if deletion is successful, - False otherwise, None if not implemented. - """ - if ids is None: - raise ValueError("No ids provided to delete.") - missing_ids = set(ids).difference(self.index_to_docstore_id.values()) - if missing_ids: - raise ValueError( - f"Some specified ids do not exist in the current store. Ids not found: " - f"{missing_ids}" - ) - - reversed_index = {id_: idx for idx, id_ in self.index_to_docstore_id.items()} - index_to_delete = [reversed_index[id_] for id_ in ids] - - self.index.remove_ids(np.array(index_to_delete, dtype=np.int64)) - self.docstore.delete(ids) - - remaining_ids = [ - id_ - for i, id_ in sorted(self.index_to_docstore_id.items()) - if i not in index_to_delete - ] - self.index_to_docstore_id = {i: id_ for i, id_ in enumerate(remaining_ids)} - - return True - - def merge_from(self, target: FAISS) -> None: - """Merge another FAISS object with the current one. - - Add the target FAISS to the current one. - - Args: - target: FAISS object you wish to merge into the current one - - Returns: - None. - """ - if not isinstance(self.docstore, AddableMixin): - raise ValueError("Cannot merge with this type of docstore") - # Numerical index for target docs are incremental on existing ones - starting_len = len(self.index_to_docstore_id) - - # Merge two IndexFlatL2 - self.index.merge_from(target.index) - - # Get id and docs from target FAISS object - full_info = [] - for i, target_id in target.index_to_docstore_id.items(): - doc = target.docstore.search(target_id) - if not isinstance(doc, Document): - raise ValueError("Document should be returned") - full_info.append((starting_len + i, target_id, doc)) - - # Add information to docstore and index_to_docstore_id. - self.docstore.add({_id: doc for _, _id, doc in full_info}) - index_to_id = {index: _id for index, _id, _ in full_info} - self.index_to_docstore_id.update(index_to_id) - - @classmethod - def __from( - cls, - texts: Iterable[str], - embeddings: List[List[float]], - embedding: Embeddings, - metadatas: Optional[Iterable[dict]] = None, - ids: Optional[List[str]] = None, - normalize_L2: bool = False, - distance_strategy: DistanceStrategy = DistanceStrategy.EUCLIDEAN_DISTANCE, - **kwargs: Any, - ) -> FAISS: - faiss = dependable_faiss_import() - if distance_strategy == DistanceStrategy.MAX_INNER_PRODUCT: - index = faiss.IndexFlatIP(len(embeddings[0])) - else: - # Default to L2, currently other metric types not initialized. - index = faiss.IndexFlatL2(len(embeddings[0])) - vecstore = cls( - embedding.embed_query, - index, - InMemoryDocstore({}), - {}, - normalize_L2=normalize_L2, - distance_strategy=distance_strategy, - **kwargs, - ) - vecstore.__add(texts, embeddings, metadatas=metadatas, ids=ids) - return vecstore - - @classmethod - def from_texts( - cls, - texts: List[str], - embedding: Embeddings, - metadatas: Optional[List[dict]] = None, - ids: Optional[List[str]] = None, - **kwargs: Any, - ) -> FAISS: - """Construct FAISS wrapper from raw documents. - - This is a user friendly interface that: - 1. Embeds documents. - 2. Creates an in memory docstore - 3. Initializes the FAISS database - - This is intended to be a quick way to get started. - - Example: - .. code-block:: python - - from langchain import FAISS - from langchain.embeddings import OpenAIEmbeddings - - embeddings = OpenAIEmbeddings() - faiss = FAISS.from_texts(texts, embeddings) - """ - from loguru import logger - embeddings = embedding.embed_documents(texts) - return cls.__from( - texts, - embeddings, - embedding, - metadatas=metadatas, - ids=ids, - **kwargs, - ) - - @classmethod - def from_embeddings( - cls, - text_embeddings: Iterable[Tuple[str, List[float]]], - embedding: Embeddings, - metadatas: Optional[Iterable[dict]] = None, - ids: Optional[List[str]] = None, - **kwargs: Any, - ) -> FAISS: - """Construct FAISS wrapper from raw documents. - - This is a user friendly interface that: - 1. Embeds documents. - 2. Creates an in memory docstore - 3. Initializes the FAISS database - - This is intended to be a quick way to get started. - - Example: - .. code-block:: python - - from langchain import FAISS - from langchain.embeddings import OpenAIEmbeddings - - embeddings = OpenAIEmbeddings() - text_embeddings = embeddings.embed_documents(texts) - text_embedding_pairs = zip(texts, text_embeddings) - faiss = FAISS.from_embeddings(text_embedding_pairs, embeddings) - """ - texts = [t[0] for t in text_embeddings] - embeddings = [t[1] for t in text_embeddings] - return cls.__from( - texts, - embeddings, - embedding, - metadatas=metadatas, - ids=ids, - **kwargs, - ) - - def save_local(self, folder_path: str, index_name: str = "index") -> None: - """Save FAISS index, docstore, and index_to_docstore_id to disk. - - Args: - folder_path: folder path to save index, docstore, - and index_to_docstore_id to. - index_name: for saving with a specific index file name - """ - path = Path(folder_path) - path.mkdir(exist_ok=True, parents=True) - - # save index separately since it is not picklable - faiss = dependable_faiss_import() - faiss.write_index( - self.index, str(path / "{index_name}.faiss".format(index_name=index_name)) - ) - - # save docstore and index_to_docstore_id - with open(path / "{index_name}.pkl".format(index_name=index_name), "wb") as f: - pickle.dump((self.docstore, self.index_to_docstore_id), f) - - @classmethod - def load_local( - cls, - folder_path: str, - embeddings: Embeddings, - index_name: str = "index", - **kwargs: Any, - ) -> FAISS: - """Load FAISS index, docstore, and index_to_docstore_id from disk. - - Args: - folder_path: folder path to load index, docstore, - and index_to_docstore_id from. - embeddings: Embeddings to use when generating queries - index_name: for saving with a specific index file name - """ - path = Path(folder_path) - # load index separately since it is not picklable - faiss = dependable_faiss_import() - index = faiss.read_index( - str(path / "{index_name}.faiss".format(index_name=index_name)) - ) - - # load docstore and index_to_docstore_id - with open(path / "{index_name}.pkl".format(index_name=index_name), "rb") as f: - docstore, index_to_docstore_id = pickle.load(f) - return cls( - embeddings.embed_query, index, docstore, index_to_docstore_id, **kwargs - ) - - def serialize_to_bytes(self) -> bytes: - """Serialize FAISS index, docstore, and index_to_docstore_id to bytes.""" - return pickle.dumps((self.index, self.docstore, self.index_to_docstore_id)) - - @classmethod - def deserialize_from_bytes( - cls, - serialized: bytes, - embeddings: Embeddings, - **kwargs: Any, - ) -> FAISS: - """Deserialize FAISS index, docstore, and index_to_docstore_id from bytes.""" - index, docstore, index_to_docstore_id = pickle.loads(serialized) - return cls( - embeddings.embed_query, index, docstore, index_to_docstore_id, **kwargs - ) - - def _select_relevance_score_fn(self) -> Callable[[float], float]: - """ - The 'correct' relevance function - may differ depending on a few things, including: - - the distance / similarity metric used by the VectorStore - - the scale of your embeddings (OpenAI's are unit normed. Many others are not!) - - embedding dimensionality - - etc. - """ - if self.override_relevance_score_fn is not None: - return self.override_relevance_score_fn - - # Default strategy is to rely on distance strategy provided in - # vectorstore constructor - if self.distance_strategy == DistanceStrategy.MAX_INNER_PRODUCT: - return self._max_inner_product_relevance_score_fn - elif self.distance_strategy == DistanceStrategy.EUCLIDEAN_DISTANCE: - # Default behavior is to use euclidean distance relevancy - return self._euclidean_relevance_score_fn - else: - raise ValueError( - "Unknown distance strategy, must be cosine, max_inner_product," - " or euclidean" - ) - - def _similarity_search_with_relevance_scores( - self, - query: str, - k: int = 4, - filter: Optional[Dict[str, Any]] = None, - fetch_k: int = 20, - **kwargs: Any, - ) -> List[Tuple[Document, float]]: - """Return docs and their similarity scores on a scale from 0 to 1.""" - # Pop score threshold so that only relevancy scores, not raw scores, are - # filtered. - relevance_score_fn = self._select_relevance_score_fn() - if relevance_score_fn is None: - raise ValueError( - "normalize_score_fn must be provided to" - " FAISS constructor to normalize scores" - ) - docs_and_scores = self.similarity_search_with_score( - query, - k=k, - filter=filter, - fetch_k=fetch_k, - **kwargs, - ) - docs_and_rel_scores = [ - (doc, relevance_score_fn(score)) for doc, score in docs_and_scores - ] - return docs_and_rel_scores diff --git a/coagent/embeddings/get_embedding.py b/coagent/embeddings/get_embedding.py deleted file mode 100644 index 34382b8..0000000 --- a/coagent/embeddings/get_embedding.py +++ /dev/null @@ -1,49 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: get_embedding.py -@time: 2023/11/22 上午11:30 -@desc: -''' -from loguru import logger - -# from configs.model_config import EMBEDDING_MODEL -from coagent.embeddings.openai_embedding import OpenAIEmbedding -from coagent.embeddings.huggingface_embedding import HFEmbedding -from coagent.llm_models.llm_config import EmbedConfig - -def get_embedding( - engine: str, - text_list: list, - model_path: str = "text2vec-base-chinese", - embedding_device: str = "cpu", - embed_config: EmbedConfig = None, - ): - ''' - get embedding - @param engine: openai / hf - @param text_list: - @return: - ''' - emb_res = {} - if embed_config and embed_config.langchain_embeddings: - emb_res = embed_config.langchain_embeddings.embed_documents(text_list) - emb_res = { - text_list[idx]: emb_res[idx] for idx in range(len(text_list)) - } - elif engine == 'openai': - oae = OpenAIEmbedding() - emb_res = oae.get_emb(text_list) - elif engine == 'model': - hfe = HFEmbedding(model_path, embedding_device) - emb_res = hfe.get_emb(text_list) - - return emb_res - - -if __name__ == '__main__': - engine = 'model' - text_list = ['这段代码是一个OkHttp拦截器,用于在请求头中添加授权令牌。它继承自`com.theokanning.openai.client.AuthenticationInterceptor`类,并且被标记为`@Deprecated`,意味着它已经过时了。\n\n这个拦截器的作用是在每个请求的头部添加一个名为"Authorization"的字段,值为传入的授权令牌。这样,当请求被发送到服务器时,服务器可以使用这个令牌来验证请求的合法性。\n\n这段代码的构造函数接受一个令牌作为参数,并将其传递给父类的构造函数。这个令牌应该是一个有效的授权令牌,用于访问受保护的资源。', '这段代码定义了一个接口`OpenAiApi`,并使用`@Deprecated`注解将其标记为已过时。它还扩展了`com.theokanning.openai.client.OpenAiApi`接口。\n\n`@Deprecated`注解表示该接口已经过时,不推荐使用。开发者应该使用`com.theokanning.openai.client.OpenAiApi`接口代替。\n\n注释中提到这个接口只是为了保持向后兼容性。这意味着它可能是为了与旧版本的代码兼容而保留的,但不推荐在新代码中使用。', '这段代码是一个OkHttp的拦截器,用于在请求头中添加授权令牌(authorization token)。\n\n在这个拦截器中,首先获取到传入的授权令牌(token),然后在每个请求的构建过程中,使用`newBuilder()`方法创建一个新的请求构建器,并在该构建器中添加一个名为"Authorization"的请求头,值为"Bearer " + token。最后,使用该构建器构建一个新的请求,并通过`chain.proceed(request)`方法继续处理该请求。\n\n这样,当使用OkHttp发送请求时,该拦截器会自动在请求头中添加授权令牌,以实现身份验证的功能。', '这段代码是一个Java接口,用于定义与OpenAI API进行通信的方法。它包含了各种不同类型的请求和响应方法,用于与OpenAI API的不同端点进行交互。\n\n接口中的方法包括:\n- `listModels()`:获取可用的模型列表。\n- `getModel(String modelId)`:获取指定模型的详细信息。\n- `createCompletion(CompletionRequest request)`:创建文本生成的请求。\n- `createChatCompletion(ChatCompletionRequest request)`:创建聊天式文本生成的请求。\n- `createEdit(EditRequest request)`:创建文本编辑的请求。\n- `createEmbeddings(EmbeddingRequest request)`:创建文本嵌入的请求。\n- `listFiles()`:获取已上传文件的列表。\n- `uploadFile(RequestBody purpose, MultipartBody.Part file)`:上传文件。\n- `deleteFile(String fileId)`:删除文件。\n- `retrieveFile(String fileId)`:获取文件的详细信息。\n- `retrieveFileContent(String fileId)`:获取文件的内容。\n- `createFineTuningJob(FineTuningJobRequest request)`:创建Fine-Tuning任务。\n- `listFineTuningJobs()`:获取Fine-Tuning任务的列表。\n- `retrieveFineTuningJob(String fineTuningJobId)`:获取指定Fine-Tuning任务的详细信息。\n- `cancelFineTuningJob(String fineTuningJobId)`:取消Fine-Tuning任务。\n- `listFineTuningJobEvents(String fineTuningJobId)`:获取Fine-Tuning任务的事件列表。\n- `createFineTuneCompletion(CompletionRequest request)`:创建Fine-Tuning模型的文本生成请求。\n- `createImage(CreateImageRequest request)`:创建图像生成的请求。\n- `createImageEdit(RequestBody requestBody)`:创建图像编辑的请求。\n- `createImageVariation(RequestBody requestBody)`:创建图像变体的请求。\n- `createTranscription(RequestBody requestBody)`:创建音频转录的请求。\n- `createTranslation(RequestBody requestBody)`:创建音频翻译的请求。\n- `createModeration(ModerationRequest request)`:创建内容审核的请求。\n- `getEngines()`:获取可用的引擎列表。\n- `getEngine(String engineId)`:获取指定引擎的详细信息。\n- `subscription()`:获取账户订阅信息。\n- `billingUsage(LocalDate starDate, LocalDate endDate)`:获取账户消费信息。\n\n这些方法使用不同的HTTP请求类型(GET、POST、DELETE)和路径来与OpenAI API进行交互,并返回相应的响应数据。'] - - res = get_embedding(engine, text_list) - logger.debug(res) \ No newline at end of file diff --git a/coagent/embeddings/huggingface_embedding.py b/coagent/embeddings/huggingface_embedding.py deleted file mode 100644 index b103753..0000000 --- a/coagent/embeddings/huggingface_embedding.py +++ /dev/null @@ -1,48 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: huggingface_embedding.py -@time: 2023/11/30 上午11:41 -@desc: -''' -from loguru import logger -# from configs.model_config import EMBEDDING_DEVICE -# from configs.model_config import embedding_model_dict -from coagent.embeddings.utils import load_embeddings, load_embeddings_from_path - - -class HFEmbedding: - _instance = {} - - def __new__(cls, *args, **kwargs): - instance_key = f'{args},{kwargs}' - - if cls._instance.get(instance_key, None): - return cls._instance[instance_key] - else: - cls._instance[instance_key] = super().__new__(cls) - return cls._instance[instance_key] - - def __init__(self, model_name, embedding_device): - self.model = load_embeddings_from_path(model_path=model_name, device=embedding_device) - logger.debug('load success') - - def get_emb(self, text_list): - ''' - get embedding - @param text_list: - @return: - ''' - emb_res = self.model.embed_documents(text_list) - res = { - text_list[idx]: emb_res[idx] for idx in range(len(text_list)) - } - return res - - -if __name__ == '__main__': - model_name = 'text2vec-base' - hfe = HFEmbedding(model_name) - text_list = ['这段代码是一个OkHttp拦截器,用于在请求头中添加授权令牌。它继承自`com.theokanning.openai.client.AuthenticationInterceptor`类,并且被标记为`@Deprecated`,意味着它已经过时了。\n\n这个拦截器的作用是在每个请求的头部添加一个名为"Authorization"的字段,值为传入的授权令牌。这样,当请求被发送到服务器时,服务器可以使用这个令牌来验证请求的合法性。\n\n这段代码的构造函数接受一个令牌作为参数,并将其传递给父类的构造函数。这个令牌应该是一个有效的授权令牌,用于访问受保护的资源。', '这段代码定义了一个接口`OpenAiApi`,并使用`@Deprecated`注解将其标记为已过时。它还扩展了`com.theokanning.openai.client.OpenAiApi`接口。\n\n`@Deprecated`注解表示该接口已经过时,不推荐使用。开发者应该使用`com.theokanning.openai.client.OpenAiApi`接口代替。\n\n注释中提到这个接口只是为了保持向后兼容性。这意味着它可能是为了与旧版本的代码兼容而保留的,但不推荐在新代码中使用。', '这段代码是一个OkHttp的拦截器,用于在请求头中添加授权令牌(authorization token)。\n\n在这个拦截器中,首先获取到传入的授权令牌(token),然后在每个请求的构建过程中,使用`newBuilder()`方法创建一个新的请求构建器,并在该构建器中添加一个名为"Authorization"的请求头,值为"Bearer " + token。最后,使用该构建器构建一个新的请求,并通过`chain.proceed(request)`方法继续处理该请求。\n\n这样,当使用OkHttp发送请求时,该拦截器会自动在请求头中添加授权令牌,以实现身份验证的功能。', '这段代码是一个Java接口,用于定义与OpenAI API进行通信的方法。它包含了各种不同类型的请求和响应方法,用于与OpenAI API的不同端点进行交互。\n\n接口中的方法包括:\n- `listModels()`:获取可用的模型列表。\n- `getModel(String modelId)`:获取指定模型的详细信息。\n- `createCompletion(CompletionRequest request)`:创建文本生成的请求。\n- `createChatCompletion(ChatCompletionRequest request)`:创建聊天式文本生成的请求。\n- `createEdit(EditRequest request)`:创建文本编辑的请求。\n- `createEmbeddings(EmbeddingRequest request)`:创建文本嵌入的请求。\n- `listFiles()`:获取已上传文件的列表。\n- `uploadFile(RequestBody purpose, MultipartBody.Part file)`:上传文件。\n- `deleteFile(String fileId)`:删除文件。\n- `retrieveFile(String fileId)`:获取文件的详细信息。\n- `retrieveFileContent(String fileId)`:获取文件的内容。\n- `createFineTuningJob(FineTuningJobRequest request)`:创建Fine-Tuning任务。\n- `listFineTuningJobs()`:获取Fine-Tuning任务的列表。\n- `retrieveFineTuningJob(String fineTuningJobId)`:获取指定Fine-Tuning任务的详细信息。\n- `cancelFineTuningJob(String fineTuningJobId)`:取消Fine-Tuning任务。\n- `listFineTuningJobEvents(String fineTuningJobId)`:获取Fine-Tuning任务的事件列表。\n- `createFineTuneCompletion(CompletionRequest request)`:创建Fine-Tuning模型的文本生成请求。\n- `createImage(CreateImageRequest request)`:创建图像生成的请求。\n- `createImageEdit(RequestBody requestBody)`:创建图像编辑的请求。\n- `createImageVariation(RequestBody requestBody)`:创建图像变体的请求。\n- `createTranscription(RequestBody requestBody)`:创建音频转录的请求。\n- `createTranslation(RequestBody requestBody)`:创建音频翻译的请求。\n- `createModeration(ModerationRequest request)`:创建内容审核的请求。\n- `getEngines()`:获取可用的引擎列表。\n- `getEngine(String engineId)`:获取指定引擎的详细信息。\n- `subscription()`:获取账户订阅信息。\n- `billingUsage(LocalDate starDate, LocalDate endDate)`:获取账户消费信息。\n\n这些方法使用不同的HTTP请求类型(GET、POST、DELETE)和路径来与OpenAI API进行交互,并返回相应的响应数据。'] - - hfe.get_emb(text_list) \ No newline at end of file diff --git a/coagent/embeddings/in_memory.py b/coagent/embeddings/in_memory.py deleted file mode 100644 index f92484d..0000000 --- a/coagent/embeddings/in_memory.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Simple in memory docstore in the form of a dict.""" -from typing import Dict, List, Optional, Union - -from langchain.docstore.base import AddableMixin, Docstore -from langchain.docstore.document import Document - - -class InMemoryDocstore(Docstore, AddableMixin): - """Simple in memory docstore in the form of a dict.""" - - def __init__(self, _dict: Optional[Dict[str, Document]] = None): - """Initialize with dict.""" - self._dict = _dict if _dict is not None else {} - - def add(self, texts: Dict[str, Document]) -> None: - """Add texts to in memory dictionary. - - Args: - texts: dictionary of id -> document. - - Returns: - None - """ - overlapping = set(texts).intersection(self._dict) - if overlapping: - raise ValueError(f"Tried to add ids that already exist: {overlapping}") - self._dict = {**self._dict, **texts} - - def delete(self, ids: List) -> None: - """Deleting IDs from in memory dictionary.""" - overlapping = set(ids).intersection(self._dict) - if not overlapping: - raise ValueError(f"Tried to delete ids that does not exist: {ids}") - for _id in ids: - self._dict.pop(_id) - - def search(self, search: str) -> Union[str, Document]: - """Search via direct lookup. - - Args: - search: id of a document to search for. - - Returns: - Document if found, else error message. - """ - if search not in self._dict: - return f"ID {search} not found." - else: - return self._dict[search] diff --git a/coagent/embeddings/openai_embedding.py b/coagent/embeddings/openai_embedding.py deleted file mode 100644 index 180f654..0000000 --- a/coagent/embeddings/openai_embedding.py +++ /dev/null @@ -1,48 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: openai_embedding.py -@time: 2023/11/22 上午10:45 -@desc: -''' -import openai -import base64 -import json -import os -from loguru import logger - - -class OpenAIEmbedding: - def __init__(self): - pass - - def get_emb(self, text_list): - openai.api_key = os.environ["OPENAI_API_KEY"] - openai.api_base = os.environ["API_BASE_URL"] - - # change , to ,to avoid bug - modified_text_list = [i.replace(',', ',') for i in text_list] - - emb_all_result = openai.Embedding.create( - model="text-embedding-ada-002", - input=modified_text_list - ) - - res = {} - # logger.debug(emb_all_result) - logger.debug(f'len of result={len(emb_all_result["data"])}') - for emb_result in emb_all_result['data']: - index = emb_result['index'] - # logger.debug(index) - text = text_list[index] - emb = emb_result['embedding'] - res[text] = emb - - return res - - -if __name__ == '__main__': - oae = OpenAIEmbedding() - res = oae.get_emb(text_list=['这段代码是一个OkHttp拦截器,用于在请求头中添加授权令牌。它继承自`com.theokanning.openai.client.AuthenticationInterceptor`类,并且被标记为`@Deprecated`,意味着它已经过时了。\n\n这个拦截器的作用是在每个请求的头部添加一个名为"Authorization"的字段,值为传入的授权令牌。这样,当请求被发送到服务器时,服务器可以使用这个令牌来验证请求的合法性。\n\n这段代码的构造函数接受一个令牌作为参数,并将其传递给父类的构造函数。这个令牌应该是一个有效的授权令牌,用于访问受保护的资源。', '这段代码定义了一个接口`OpenAiApi`,并使用`@Deprecated`注解将其标记为已过时。它还扩展了`com.theokanning.openai.client.OpenAiApi`接口。\n\n`@Deprecated`注解表示该接口已经过时,不推荐使用。开发者应该使用`com.theokanning.openai.client.OpenAiApi`接口代替。\n\n注释中提到这个接口只是为了保持向后兼容性。这意味着它可能是为了与旧版本的代码兼容而保留的,但不推荐在新代码中使用。', '这段代码是一个OkHttp的拦截器,用于在请求头中添加授权令牌(authorization token)。\n\n在这个拦截器中,首先获取到传入的授权令牌(token),然后在每个请求的构建过程中,使用`newBuilder()`方法创建一个新的请求构建器,并在该构建器中添加一个名为"Authorization"的请求头,值为"Bearer " + token。最后,使用该构建器构建一个新的请求,并通过`chain.proceed(request)`方法继续处理该请求。\n\n这样,当使用OkHttp发送请求时,该拦截器会自动在请求头中添加授权令牌,以实现身份验证的功能。', '这段代码是一个Java接口,用于定义与OpenAI API进行通信的方法。它包含了各种不同类型的请求和响应方法,用于与OpenAI API的不同端点进行交互。\n\n接口中的方法包括:\n- `listModels()`:获取可用的模型列表。\n- `getModel(String modelId)`:获取指定模型的详细信息。\n- `createCompletion(CompletionRequest request)`:创建文本生成的请求。\n- `createChatCompletion(ChatCompletionRequest request)`:创建聊天式文本生成的请求。\n- `createEdit(EditRequest request)`:创建文本编辑的请求。\n- `createEmbeddings(EmbeddingRequest request)`:创建文本嵌入的请求。\n- `listFiles()`:获取已上传文件的列表。\n- `uploadFile(RequestBody purpose, MultipartBody.Part file)`:上传文件。\n- `deleteFile(String fileId)`:删除文件。\n- `retrieveFile(String fileId)`:获取文件的详细信息。\n- `retrieveFileContent(String fileId)`:获取文件的内容。\n- `createFineTuningJob(FineTuningJobRequest request)`:创建Fine-Tuning任务。\n- `listFineTuningJobs()`:获取Fine-Tuning任务的列表。\n- `retrieveFineTuningJob(String fineTuningJobId)`:获取指定Fine-Tuning任务的详细信息。\n- `cancelFineTuningJob(String fineTuningJobId)`:取消Fine-Tuning任务。\n- `listFineTuningJobEvents(String fineTuningJobId)`:获取Fine-Tuning任务的事件列表。\n- `createFineTuneCompletion(CompletionRequest request)`:创建Fine-Tuning模型的文本生成请求。\n- `createImage(CreateImageRequest request)`:创建图像生成的请求。\n- `createImageEdit(RequestBody requestBody)`:创建图像编辑的请求。\n- `createImageVariation(RequestBody requestBody)`:创建图像变体的请求。\n- `createTranscription(RequestBody requestBody)`:创建音频转录的请求。\n- `createTranslation(RequestBody requestBody)`:创建音频翻译的请求。\n- `createModeration(ModerationRequest request)`:创建内容审核的请求。\n- `getEngines()`:获取可用的引擎列表。\n- `getEngine(String engineId)`:获取指定引擎的详细信息。\n- `subscription()`:获取账户订阅信息。\n- `billingUsage(LocalDate starDate, LocalDate endDate)`:获取账户消费信息。\n\n这些方法使用不同的HTTP请求类型(GET、POST、DELETE)和路径来与OpenAI API进行交互,并返回相应的响应数据。']) - # res = oae.get_emb(text_list=['''test1"test2test3''', '''test4test5test6''']) - print(res) diff --git a/coagent/embeddings/utils.py b/coagent/embeddings/utils.py deleted file mode 100644 index 25088b1..0000000 --- a/coagent/embeddings/utils.py +++ /dev/null @@ -1,25 +0,0 @@ -import os -from functools import lru_cache -from langchain.embeddings.huggingface import HuggingFaceEmbeddings -from langchain.embeddings.base import Embeddings - -# from configs.model_config import embedding_model_dict -from loguru import logger - - -@lru_cache(1) -def load_embeddings(model: str, device: str, embedding_model_dict: dict): - embeddings = HuggingFaceEmbeddings(model_name=embedding_model_dict[model], - model_kwargs={'device': device}) - return embeddings - - -# @lru_cache(1) -def load_embeddings_from_path(model_path: str, device: str, langchain_embeddings: Embeddings = None): - if langchain_embeddings: - return langchain_embeddings - - embeddings = HuggingFaceEmbeddings(model_name=model_path, - model_kwargs={'device': device}) - return embeddings - diff --git a/coagent/llm_models/__init__.py b/coagent/llm_models/__init__.py deleted file mode 100644 index 11bbe7a..0000000 --- a/coagent/llm_models/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .openai_model import getExtraModel, getChatModelFromConfig -from .llm_config import LLMConfig, EmbedConfig - - -__all__ = [ - "getExtraModel", "getChatModelFromConfig", - "LLMConfig", "EmbedConfig" -] \ No newline at end of file diff --git a/coagent/llm_models/llm_config.py b/coagent/llm_models/llm_config.py deleted file mode 100644 index 9dac682..0000000 --- a/coagent/llm_models/llm_config.py +++ /dev/null @@ -1,68 +0,0 @@ -from dataclasses import dataclass -from typing import List, Union - -from langchain.embeddings.base import Embeddings -from langchain.llms.base import LLM, BaseLLM - - - -@dataclass -class LLMConfig: - def __init__( - self, - model_name: str = "gpt-3.5-turbo", - temperature: float = 0.25, - stop: Union[List[str], str] = None, - api_key: str = "", - api_base_url: str = "", - model_device: str = "cpu", # unuse,will delete it - llm: LLM = None, - **kwargs - ): - - self.model_name: str = model_name - self.temperature: float = temperature - self.stop: Union[List[str], str] = stop - self.api_key: str = api_key - self.api_base_url: str = api_base_url - self.llm: LLM = llm - # - self.check_config() - - def check_config(self, ): - pass - - def __str__(self): - return ', '.join(f"{k}: {v}" for k,v in vars(self).items()) - - -@dataclass -class EmbedConfig: - def __init__( - self, - api_key: str = "", - api_base_url: str = "", - embed_model: str = "", - embed_model_path: str = "", - embed_engine: str = "", - model_device: str = "cpu", - langchain_embeddings: Embeddings = None, - **kwargs - ): - self.embed_model: str = embed_model - self.embed_model_path: str = embed_model_path - self.embed_engine: str = embed_engine - self.model_device: str = model_device - self.api_key: str = api_key - self.api_base_url: str = api_base_url - # - self.langchain_embeddings = langchain_embeddings - # - self.check_config() - - def check_config(self, ): - pass - - def __str__(self): - return ', '.join(f"{k}: {v}" for k,v in vars(self).items()) - \ No newline at end of file diff --git a/coagent/llm_models/openai_model.py b/coagent/llm_models/openai_model.py deleted file mode 100644 index 50453c9..0000000 --- a/coagent/llm_models/openai_model.py +++ /dev/null @@ -1,94 +0,0 @@ -import os -from typing import Union, Optional, List -from loguru import logger - -from langchain.callbacks import AsyncIteratorCallbackHandler -from langchain.chat_models import ChatOpenAI -from langchain.llms.base import LLM - -from .llm_config import LLMConfig -# from configs.model_config import (llm_model_dict, LLM_MODEL) - - -class CustomLLMModel: - - def __init__(self, llm: LLM): - self.llm: LLM = llm - - def __call__(self, prompt: str, - stop: Optional[List[str]] = None): - return self.llm(prompt, stop) - - def _call(self, prompt: str, - stop: Optional[List[str]] = None): - return self.llm(prompt, stop) - - def predict(self, prompt: str, - stop: Optional[List[str]] = None): - return self.llm(prompt, stop) - - def batch(self, prompts: str, - stop: Optional[List[str]] = None): - return [self.llm(prompt, stop) for prompt in prompts] - - -def getChatModelFromConfig(llm_config: LLMConfig, callBack: AsyncIteratorCallbackHandler = None, ) -> Union[ChatOpenAI, LLM]: - # logger.debug(f"llm type is {type(llm_config.llm)}") - if llm_config is None: - model = ChatOpenAI( - streaming=True, - verbose=True, - openai_api_key=os.environ.get("api_key"), - openai_api_base=os.environ.get("api_base_url"), - model_name=os.environ.get("LLM_MODEL", "gpt-3.5-turbo"), - temperature=os.environ.get("temperature", 0.5), - stop=os.environ.get("stop", ""), - ) - return model - - if llm_config and llm_config.llm and isinstance(llm_config.llm, LLM): - return CustomLLMModel(llm=llm_config.llm) - - if callBack is None: - model = ChatOpenAI( - streaming=True, - verbose=True, - openai_api_key=llm_config.api_key, - openai_api_base=llm_config.api_base_url, - model_name=llm_config.model_name, - temperature=llm_config.temperature, - stop=llm_config.stop - ) - else: - model = ChatOpenAI( - streaming=True, - verbose=True, - callBack=[callBack], - openai_api_key=llm_config.api_key, - openai_api_base=llm_config.api_base_url, - model_name=llm_config.model_name, - temperature=llm_config.temperature, - stop=llm_config.stop - ) - - return model - - -import json, requests - - -def getExtraModel(): - return TestModel() - -class TestModel: - - def predict(self, request_body): - headers = {"Content-Type":"application/json;charset=UTF-8", - "codegpt_user":"", - "codegpt_token":"" - } - xxx = requests.post( - 'https://codegencore.alipay.com/api/chat/CODE_LLAMA_INT4/completion', - data=json.dumps(request_body,ensure_ascii=False).encode('utf-8'), - headers=headers) - return xxx.json()["data"] \ No newline at end of file diff --git a/coagent/orm/__init__.py b/coagent/orm/__init__.py deleted file mode 100644 index 2a2c21b..0000000 --- a/coagent/orm/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -from .db import _engine, Base -from loguru import logger - -__all__ = [ - -] - -def create_tables(): - Base.metadata.create_all(bind=_engine) - -def reset_tables(): - Base.metadata.drop_all(bind=_engine) - create_tables() - - -def check_tables_exist(table_name) -> bool: - table_exist = _engine.dialect.has_table(_engine.connect(), table_name, schema=None) - return table_exist - -def table_init(): - if (not check_tables_exist("knowledge_base")) or (not check_tables_exist ("knowledge_file")) or \ - (not check_tables_exist ("code_base")): - create_tables() diff --git a/coagent/orm/commands/__init__.py b/coagent/orm/commands/__init__.py deleted file mode 100644 index dfda957..0000000 --- a/coagent/orm/commands/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -from .document_file_cds import * -from .document_base_cds import * -from .code_base_cds import * - -__all__ = [ - "add_kb_to_db", "list_kbs_from_db", "kb_exists", - "load_kb_from_db", "delete_kb_from_db", "get_kb_detail", - - "list_docs_from_db", "add_doc_to_db", "delete_file_from_db", - "delete_files_from_db", "doc_exists", "get_file_detail", - - "list_cbs_from_db", "add_cb_to_db", "delete_cb_from_db", - "cb_exists", "get_cb_detail", -] \ No newline at end of file diff --git a/coagent/orm/commands/code_base_cds.py b/coagent/orm/commands/code_base_cds.py deleted file mode 100644 index cb3c161..0000000 --- a/coagent/orm/commands/code_base_cds.py +++ /dev/null @@ -1,82 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: code_base_cds.py.py -@time: 2023/10/23 下午4:34 -@desc: -''' -from loguru import logger -from coagent.orm.db import with_session -from coagent.orm.schemas.base_schema import CodeBaseSchema - - -@with_session -def add_cb_to_db(session, code_name, code_path, code_graph_node_num, code_file_num, do_interpret): - do_interpret = 'YES' if do_interpret else 'NO' - - # 增:创建知识库实例 - cb = session.query(CodeBaseSchema).filter_by(code_name=code_name).first() - if not cb: - cb = CodeBaseSchema(code_name=code_name, code_path=code_path, code_graph_node_num=code_graph_node_num, - code_file_num=code_file_num, do_interpret=do_interpret) - session.add(cb) - else: - cb.code_path = code_path - cb.code_graph_node_num = code_graph_node_num - return True - - -@with_session -def list_cbs_from_db(session): - ''' - 查:查询实例 - ''' - cbs = session.query(CodeBaseSchema.code_name).all() - cbs = [cb[0] for cb in cbs] - return cbs - - -@with_session -def cb_exists(session, code_name): - ''' - 判断是否存在 - ''' - cb = session.query(CodeBaseSchema).filter_by(code_name=code_name).first() - status = True if cb else False - return status - -@with_session -def load_cb_from_db(session, code_name): - cb = session.query(CodeBaseSchema).filter_by(code_name=code_name).first() - if cb: - code_name, code_path, code_graph_node_num, do_interpret = cb.code_name, cb.code_path, cb.code_graph_node_num, cb.do_interpret - else: - code_name, code_path, code_graph_node_num = None, None, None, None - return code_name, code_path, code_graph_node_num, do_interpret - - -@with_session -def delete_cb_from_db(session, code_name): - cb = session.query(CodeBaseSchema).filter_by(code_name=code_name).first() - if cb: - session.delete(cb) - return True - - -@with_session -def get_cb_detail(session, code_name: str) -> dict: - cb: CodeBaseSchema = session.query(CodeBaseSchema).filter_by(code_name=code_name).first() - logger.info(cb) - logger.info('code_name={}'.format(cb.code_name)) - if cb: - return { - "code_name": cb.code_name, - "code_path": cb.code_path, - "code_graph_node_num": cb.code_graph_node_num, - 'code_file_num': cb.code_file_num, - 'do_interpret': cb.do_interpret - } - else: - return { - } - diff --git a/coagent/orm/commands/document_base_cds.py b/coagent/orm/commands/document_base_cds.py deleted file mode 100644 index 2091abe..0000000 --- a/coagent/orm/commands/document_base_cds.py +++ /dev/null @@ -1,89 +0,0 @@ -from coagent.orm.db import with_session -from coagent.orm.schemas.base_schema import KnowledgeBaseSchema - - -# @with_session -# def _query_by_condition(session, schema, query_kargs, query_type="first"): -# if len(query_kargs) >0: -# if query_type == "first": -# return session.query(schema).filter_by(query_kargs).first() -# elif query_type == "all": -# return session.query(schema).filter_by(query_kargs).first() - -# @with_session -# def _add_to_db(session, schema, query_kargs): -# kb = schema(**query_kargs) -# session.add(kb) -# return True - -# @with_session -# def add_to_db(session, schema, query_kargs): -# kb = _query_by_condition(session, schema, query_kargs, query_type="first") -# if not kb: -# _add_to_db(session, schema, query_kargs) -# else: # update kb with new vs_type and embed_model -# for k, v in query_kargs.items(): -# if k in kb: -# kb[k] = v -# return True - - - -@with_session -def add_kb_to_db(session, kb_name, vs_type, embed_model): - # 创建知识库实例 - kb = session.query(KnowledgeBaseSchema).filter_by(kb_name=kb_name).first() - if not kb: - kb = KnowledgeBaseSchema(kb_name=kb_name, vs_type=vs_type, embed_model=embed_model) - session.add(kb) - else: # update kb with new vs_type and embed_model - kb.vs_type = vs_type - kb.embed_model = embed_model - return True - - -@with_session -def list_kbs_from_db(session, min_file_count: int = -1): - kbs = session.query(KnowledgeBaseSchema.kb_name).filter(KnowledgeBaseSchema.file_count > min_file_count).all() - kbs = [kb[0] for kb in kbs] - return kbs - - -@with_session -def kb_exists(session, kb_name): - kb = session.query(KnowledgeBaseSchema).filter_by(kb_name=kb_name).first() - status = True if kb else False - return status - - -@with_session -def load_kb_from_db(session, kb_name): - kb = session.query(KnowledgeBaseSchema).filter_by(kb_name=kb_name).first() - if kb: - kb_name, vs_type, embed_model = kb.kb_name, kb.vs_type, kb.embed_model - else: - kb_name, vs_type, embed_model = None, None, None - return kb_name, vs_type, embed_model - - -@with_session -def delete_kb_from_db(session, kb_name): - kb = session.query(KnowledgeBaseSchema).filter_by(kb_name=kb_name).first() - if kb: - session.delete(kb) - return True - - -@with_session -def get_kb_detail(session, kb_name: str) -> dict: - kb: KnowledgeBaseSchema = session.query(KnowledgeBaseSchema).filter_by(kb_name=kb_name).first() - if kb: - return { - "kb_name": kb.kb_name, - "vs_type": kb.vs_type, - "embed_model": kb.embed_model, - "file_count": kb.file_count, - "create_time": kb.create_time, - } - else: - return {} diff --git a/coagent/orm/commands/document_file_cds.py b/coagent/orm/commands/document_file_cds.py deleted file mode 100644 index a17d43f..0000000 --- a/coagent/orm/commands/document_file_cds.py +++ /dev/null @@ -1,87 +0,0 @@ -from coagent.orm.db import with_session -from coagent.orm.schemas.base_schema import KnowledgeFileSchema, KnowledgeBaseSchema -from coagent.orm.utils import DocumentFile - - - -@with_session -def list_docs_from_db(session, kb_name): - files = session.query(KnowledgeFileSchema).filter_by(kb_name=kb_name).all() - docs = [f.file_name for f in files] - return docs - - -@with_session -def add_doc_to_db(session, kb_file: DocumentFile): - kb = session.query(KnowledgeBaseSchema).filter_by(kb_name=kb_file.kb_name).first() - if kb: - # 如果已经存在该文件,则更新文件版本号 - existing_file = session.query(KnowledgeFileSchema).filter_by(file_name=kb_file.filename, - kb_name=kb_file.kb_name).first() - if existing_file: - existing_file.file_version += 1 - # 否则,添加新文件 - else: - new_file = KnowledgeFileSchema( - file_name=kb_file.filename, - file_ext=kb_file.ext, - kb_name=kb_file.kb_name, - document_loader_name=kb_file.document_loader_name, - text_splitter_name=kb_file.text_splitter_name or "SpacyTextSplitter", - ) - kb.file_count += 1 - session.add(new_file) - return True - - -@with_session -def delete_file_from_db(session, kb_file: DocumentFile): - existing_file = session.query(KnowledgeFileSchema).filter_by(file_name=kb_file.filename, - kb_name=kb_file.kb_name).first() - if existing_file: - session.delete(existing_file) - session.commit() - - kb = session.query(KnowledgeBaseSchema).filter_by(kb_name=kb_file.kb_name).first() - if kb: - kb.file_count -= 1 - session.commit() - return True - - -@with_session -def delete_files_from_db(session, knowledge_base_name: str): - session.query(KnowledgeFileSchema).filter_by(kb_name=knowledge_base_name).delete() - - kb = session.query(KnowledgeBaseSchema).filter_by(kb_name=knowledge_base_name).first() - if kb: - kb.file_count = 0 - - session.commit() - return True - - -@with_session -def doc_exists(session, kb_file: DocumentFile): - existing_file = session.query(KnowledgeFileSchema).filter_by(file_name=kb_file.filename, - kb_name=kb_file.kb_name).first() - return True if existing_file else False - - -@with_session -def get_file_detail(session, kb_name: str, filename: str) -> dict: - file: KnowledgeFileSchema = (session.query(KnowledgeFileSchema) - .filter_by(file_name=filename, - kb_name=kb_name).first()) - if file: - return { - "kb_name": file.kb_name, - "file_name": file.file_name, - "file_ext": file.file_ext, - "file_version": file.file_version, - "document_loader": file.document_loader_name, - "text_splitter": file.text_splitter_name, - "create_time": file.create_time, - } - else: - return {} diff --git a/coagent/orm/db.py b/coagent/orm/db.py deleted file mode 100644 index ac7e3b1..0000000 --- a/coagent/orm/db.py +++ /dev/null @@ -1,60 +0,0 @@ -from contextlib import contextmanager -from sqlalchemy.engine import create_engine -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker - -from coagent.base_configs.env_config import SQLALCHEMY_DATABASE_URI -# from configs.model_config import SQLALCHEMY_DATABASE_URI - - -_engine = create_engine(SQLALCHEMY_DATABASE_URI) - -session_factory = sessionmaker(bind=_engine) - -Base = declarative_base() - - - -def init_session(): - session = session_factory() - - try: - yield session - finally: - try: - session.commit() - except Exception as e: - session.rollback() - raise e - finally: - session.close() - - - -def with_session(func): - def wrapper(*args, **kwargs): - session = session_factory() - try: - return func(session, *args, **kwargs) - finally: - try: - session.commit() - except Exception as e: - session.rollback() - raise e - finally: - session.close() - return wrapper - - -@contextmanager -def session_scope(): - """上下文管理器用于自动获取 Session, 避免错误""" - session = session_factory(autoflush=True) - try: - session.commit() - except Exception as e: - session.rollback() - raise e - finally: - session.close() diff --git a/coagent/orm/schemas/__init__.py b/coagent/orm/schemas/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/coagent/orm/schemas/base_schema.py b/coagent/orm/schemas/base_schema.py deleted file mode 100644 index 775d7a1..0000000 --- a/coagent/orm/schemas/base_schema.py +++ /dev/null @@ -1,71 +0,0 @@ -from sqlalchemy import Column, Integer, String, DateTime, func - -from coagent.orm.db import Base - - -class KnowledgeBaseSchema(Base): - """ - 知识库模型 - """ - __tablename__ = 'knowledge_base' - id = Column(Integer, primary_key=True, autoincrement=True, comment='知识库ID') - kb_name = Column(String, comment='知识库名称') - vs_type = Column(String, comment='嵌入模型类型') - embed_model = Column(String, comment='嵌入模型名称') - file_count = Column(Integer, default=0, comment='文件数量') - create_time = Column(DateTime, default=func.now(), comment='创建时间') - - def __repr__(self): - return f"""""" - -class KnowledgeFileSchema(Base): - """ - 知识文件模型 - """ - __tablename__ = 'knowledge_file' - id = Column(Integer, primary_key=True, autoincrement=True, comment='知识文件ID') - file_name = Column(String, comment='文件名') - file_ext = Column(String, comment='文件扩展名') - kb_name = Column(String, comment='所属知识库名称') - document_loader_name = Column(String, comment='文档加载器名称') - text_splitter_name = Column(String, comment='文本分割器名称') - file_version = Column(Integer, default=1, comment='文件版本') - create_time = Column(DateTime, default=func.now(), comment='创建时间') - - def __repr__(self): - return f"""""" - - -class CodeBaseSchema(Base): - ''' - 代码数据库模型 - ''' - __tablename__ = 'code_base' - id = Column(Integer, primary_key=True, autoincrement=True, comment='代码库 ID') - code_name = Column(String, comment='代码库名称') - code_path = Column(String, comment='代码本地路径') - code_graph_node_num = Column(String, comment='代码图谱节点数') - code_file_num = Column(String, comment='代码解析文件数') - do_interpret = Column(String, comment='是否代码解读,Y or N') - create_time = Column(DateTime, default=func.now(), comment='创建时间') - - def __repr__(self): - return f"""""" diff --git a/coagent/orm/utils.py b/coagent/orm/utils.py deleted file mode 100644 index cf52498..0000000 --- a/coagent/orm/utils.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -from coagent.utils.path_utils import get_file_path, get_LoaderClass, SUPPORTED_EXTS -# from configs.model_config import KB_ROOT_PATH -from loguru import logger - - -class DocumentFile: - def __init__( - self, filename: str, knowledge_base_name: str, kb_root_path: str) -> None: - self.kb_name = knowledge_base_name - self.filename = filename - self.filename = os.path.basename(filename) - self.ext = os.path.splitext(filename)[-1].lower() - self.kb_root_path = kb_root_path - if self.ext not in SUPPORTED_EXTS: - raise ValueError(f"暂未支持的文件格式 {self.ext}") - self.filepath = get_file_path(knowledge_base_name, self.filename, kb_root_path) - self.docs = None - self.document_loader_name = get_LoaderClass(self.ext) - - # TODO: 增加依据文件格式匹配text_splitter - self.text_splitter_name = None \ No newline at end of file diff --git a/coagent/retrieval/__init__.py b/coagent/retrieval/__init__.py deleted file mode 100644 index 193bb2e..0000000 --- a/coagent/retrieval/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# from .base_retrieval import * - -# __all__ = [ -# "IMRertrieval", "BaseDocRetrieval", "BaseCodeRetrieval", "BaseSearchRetrieval" -# ] \ No newline at end of file diff --git a/coagent/retrieval/base_retrieval.py b/coagent/retrieval/base_retrieval.py deleted file mode 100644 index 79279c6..0000000 --- a/coagent/retrieval/base_retrieval.py +++ /dev/null @@ -1,75 +0,0 @@ - -from coagent.llm_models.llm_config import EmbedConfig, LLMConfig -from coagent.base_configs.env_config import KB_ROOT_PATH -from coagent.tools import DocRetrieval, CodeRetrieval - - -class IMRertrieval: - - def __init__(self,): - ''' - init your personal attributes - ''' - pass - - def run(self, ): - ''' - execute interface, and can use init' attributes - ''' - pass - - -class BaseDocRetrieval(IMRertrieval): - - def __init__(self, knowledge_base_name: str, search_top=5, score_threshold=1.0, embed_config: EmbedConfig=EmbedConfig(), kb_root_path: str=KB_ROOT_PATH): - self.knowledge_base_name = knowledge_base_name - self.search_top = search_top - self.score_threshold = score_threshold - self.embed_config = embed_config - self.kb_root_path = kb_root_path - - def run(self, query: str, search_top=None, score_threshold=None, ): - docs = DocRetrieval.run( - query=query, knowledge_base_name=self.knowledge_base_name, - search_top=search_top or self.search_top, - score_threshold=score_threshold or self.score_threshold, - embed_config=self.embed_config, - kb_root_path=self.kb_root_path - ) - return docs - - -class BaseCodeRetrieval(IMRertrieval): - - def __init__(self, code_base_name, embed_config: EmbedConfig, llm_config: LLMConfig, search_type = 'tag', code_limit = 1, local_graph_path: str=""): - self.code_base_name = code_base_name - self.embed_config = embed_config - self.llm_config = llm_config - self.search_type = search_type - self.code_limit = code_limit - self.use_nh: bool = False - self.local_graph_path: str = local_graph_path - - def run(self, query, history_node_list=[], search_type = None, code_limit=None): - code_docs = CodeRetrieval.run( - code_base_name=self.code_base_name, - query=query, - history_node_list=history_node_list, - code_limit=code_limit or self.code_limit, - search_type=search_type or self.search_type, - llm_config=self.llm_config, - embed_config=self.embed_config, - use_nh=self.use_nh, - local_graph_path=self.local_graph_path - ) - return code_docs - - - -class BaseSearchRetrieval(IMRertrieval): - - def __init__(self, ): - pass - - def run(self, ): - pass diff --git a/coagent/retrieval/document_loaders/__init__.py b/coagent/retrieval/document_loaders/__init__.py deleted file mode 100644 index 2343aeb..0000000 --- a/coagent/retrieval/document_loaders/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .json_loader import JSONLoader -from .jsonl_loader import JSONLLoader - -__all__ = [ - "JSONLoader", "JSONLLoader" -] \ No newline at end of file diff --git a/coagent/retrieval/document_loaders/json_loader.py b/coagent/retrieval/document_loaders/json_loader.py deleted file mode 100644 index 4e5ecd3..0000000 --- a/coagent/retrieval/document_loaders/json_loader.py +++ /dev/null @@ -1,61 +0,0 @@ -import json -from pathlib import Path -from typing import AnyStr, Callable, Dict, List, Optional, Union - -from langchain.docstore.document import Document -from langchain.document_loaders.base import BaseLoader -from langchain.text_splitter import RecursiveCharacterTextSplitter, TextSplitter - -from coagent.utils.common_utils import read_json_file - - -class JSONLoader(BaseLoader): - - def __init__( - self, - file_path: Union[str, Path], - schema_key: str = "all_text", - content_key: Optional[str] = None, - metadata_func: Optional[Callable[[Dict, Dict], Dict]] = None, - text_content: bool = True, - ): - self.file_path = Path(file_path).resolve() - self.schema_key = schema_key - self._content_key = content_key - self._metadata_func = metadata_func - self._text_content = text_content - - def load(self, ) -> List[Document]: - """Load and return documents from the JSON file.""" - docs: List[Document] = [] - datas = read_json_file(self.file_path) - self._parse(datas, docs) - return docs - - def _parse(self, datas: List, docs: List[Document]) -> None: - for idx, sample in enumerate(datas): - metadata = dict( - source=str(self.file_path), - seq_num=idx, - ) - text = sample.get(self.schema_key, "") - docs.append(Document(page_content=text, metadata=metadata)) - - def load_and_split( - self, text_splitter: Optional[TextSplitter] = None - ) -> List[Document]: - """Load Documents and split into chunks. Chunks are returned as Documents. - - Args: - text_splitter: TextSplitter instance to use for splitting documents. - Defaults to RecursiveCharacterTextSplitter. - - Returns: - List of Documents. - """ - if text_splitter is None: - _text_splitter: TextSplitter = RecursiveCharacterTextSplitter() - else: - _text_splitter = text_splitter - docs = self.load() - return _text_splitter.split_documents(docs) \ No newline at end of file diff --git a/coagent/retrieval/document_loaders/jsonl_loader.py b/coagent/retrieval/document_loaders/jsonl_loader.py deleted file mode 100644 index a56e6eb..0000000 --- a/coagent/retrieval/document_loaders/jsonl_loader.py +++ /dev/null @@ -1,62 +0,0 @@ -import json -from pathlib import Path -from typing import AnyStr, Callable, Dict, List, Optional, Union - -from langchain.docstore.document import Document -from langchain.document_loaders.base import BaseLoader -from langchain.text_splitter import RecursiveCharacterTextSplitter, TextSplitter - -from coagent.utils.common_utils import read_jsonl_file - - -class JSONLLoader(BaseLoader): - - def __init__( - self, - file_path: Union[str, Path], - schema_key: str = "all_text", - content_key: Optional[str] = None, - metadata_func: Optional[Callable[[Dict, Dict], Dict]] = None, - text_content: bool = True, - ): - self.file_path = Path(file_path).resolve() - self.schema_key = schema_key - self._content_key = content_key - self._metadata_func = metadata_func - self._text_content = text_content - - def load(self, ) -> List[Document]: - """Load and return documents from the JSON file.""" - docs: List[Document] = [] - datas = read_jsonl_file(self.file_path) - self._parse(datas, docs) - return docs - - def _parse(self, datas: List, docs: List[Document]) -> None: - for idx, sample in enumerate(datas): - metadata = dict( - source=str(self.file_path), - seq_num=idx, - ) - text = sample.get(self.schema_key, "") - docs.append(Document(page_content=text, metadata=metadata)) - - def load_and_split( - self, text_splitter: Optional[TextSplitter] = None - ) -> List[Document]: - """Load Documents and split into chunks. Chunks are returned as Documents. - - Args: - text_splitter: TextSplitter instance to use for splitting documents. - Defaults to RecursiveCharacterTextSplitter. - - Returns: - List of Documents. - """ - if text_splitter is None: - _text_splitter: TextSplitter = RecursiveCharacterTextSplitter() - else: - _text_splitter = text_splitter - - docs = self.load() - return _text_splitter.split_documents(docs) \ No newline at end of file diff --git a/coagent/retrieval/text_splitter/__init__.py b/coagent/retrieval/text_splitter/__init__.py deleted file mode 100644 index 8867d74..0000000 --- a/coagent/retrieval/text_splitter/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .langchain_splitter import LCTextSplitter - -__all__ = ["LCTextSplitter"] \ No newline at end of file diff --git a/coagent/retrieval/text_splitter/langchain_splitter.py b/coagent/retrieval/text_splitter/langchain_splitter.py deleted file mode 100644 index 4b53025..0000000 --- a/coagent/retrieval/text_splitter/langchain_splitter.py +++ /dev/null @@ -1,77 +0,0 @@ -import os -import importlib -from loguru import logger - -from langchain.document_loaders.base import BaseLoader -from langchain.text_splitter import ( - SpacyTextSplitter, RecursiveCharacterTextSplitter -) - -# from configs.model_config import ( -# CHUNK_SIZE, -# OVERLAP_SIZE, -# ZH_TITLE_ENHANCE -# ) -from coagent.utils.path_utils import * - - - -class LCTextSplitter: - '''langchain textsplitter 执行file2text''' - def __init__( - self, filepath: str, text_splitter_name: str = None, - chunk_size: int = 500, - overlap_size: int = 50 - ): - self.filepath = filepath - self.ext = os.path.splitext(filepath)[-1].lower() - self.text_splitter_name = text_splitter_name - self.chunk_size = chunk_size - self.overlap_size = overlap_size - if self.ext not in SUPPORTED_EXTS: - raise ValueError(f"暂未支持的文件格式 {self.ext}") - self.document_loader_name = get_LoaderClass(self.ext) - - def file2text(self, ): - loader = self._load_document() - text_splitter = self._load_text_splitter() - if self.document_loader_name in ["JSONLoader", "JSONLLoader"]: - # docs = loader.load() - docs = loader.load_and_split(text_splitter) - # logger.debug(f"please check your file can be loaded, docs.lens {len(docs)}") - else: - docs = loader.load_and_split(text_splitter) - - return docs - - def _load_document(self, ) -> BaseLoader: - DocumentLoader = EXT2LOADER_DICT[self.ext] - if self.document_loader_name == "UnstructuredFileLoader": - loader = DocumentLoader(self.filepath, autodetect_encoding=True) - else: - loader = DocumentLoader(self.filepath) - return loader - - def _load_text_splitter(self, ): - try: - if self.text_splitter_name is None: - text_splitter = SpacyTextSplitter( - pipeline="zh_core_web_sm", - chunk_size=self.chunk_size, - chunk_overlap=self.overlap_size, - ) - self.text_splitter_name = "SpacyTextSplitter" - # elif self.document_loader_name in ["JSONLoader", "JSONLLoader"]: - # text_splitter = None - else: - text_splitter_module = importlib.import_module('langchain.text_splitter') - TextSplitter = getattr(text_splitter_module, self.text_splitter_name) - text_splitter = TextSplitter( - chunk_size=self.chunk_size, - chunk_overlap=self.overlap_size) - except Exception as e: - text_splitter = RecursiveCharacterTextSplitter( - chunk_size=self.chunk_size, - chunk_overlap=self.overlap_size, - ) - return text_splitter diff --git a/coagent/retrieval/text_splitter/utils.py b/coagent/retrieval/text_splitter/utils.py deleted file mode 100644 index e69de29..0000000 diff --git a/coagent/sandbox/__init__.py b/coagent/sandbox/__init__.py deleted file mode 100644 index 435da9b..0000000 --- a/coagent/sandbox/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .basebox import CodeBoxResponse -from .pycodebox import PyCodeBox - -__all__ = [ - "CodeBoxResponse", "PyCodeBox" -] \ No newline at end of file diff --git a/coagent/sandbox/basebox.py b/coagent/sandbox/basebox.py deleted file mode 100644 index c276f1d..0000000 --- a/coagent/sandbox/basebox.py +++ /dev/null @@ -1,125 +0,0 @@ -from pydantic import BaseModel -from typing import Optional -from pathlib import Path -import sys -from abc import ABC, abstractclassmethod -from loguru import logger - -class CodeBoxResponse(BaseModel): - code_text: str = "" - code_exe_response: str = "" - code_exe_type: str = "" - code_exe_status: int - do_code_exe: bool - - def __str__(self,): - return f"""status: {self.code_exe_status}, type: {self.code_exe_type}, response: {self.code_exe_response}""" - - -class CodeBoxStatus(BaseModel): - status: str - - -class BaseBox(ABC): - - enter_status = False - - def __init__( - self, - remote_url: str = "", - remote_ip: str = "htpp://127.0.0.1", - remote_port: str = "5050", - token: str = "mytoken", - do_code_exe: bool = False, - do_remote: bool = False - ): - self.token = token - self.remote_url = remote_url or remote_ip + ":" + str(remote_port) - self.remote_ip = remote_ip - self.remote_port = remote_port - self.do_code_exe = do_code_exe - self.do_remote = do_remote - self.local_pyenv = Path(sys.executable).absolute() - self.ws = None - self.aiohttp_session = None - self.kernel_url = "" - - def chat(self, text: str, file_path: str = None, do_code_exe: bool = None) -> CodeBoxResponse: - '''执行流''' - do_code_exe = self.do_code_exe if do_code_exe is None else do_code_exe - if not do_code_exe: - return CodeBoxResponse( - code_exe_response=text, code_text=text, code_exe_type="text", code_exe_status=200, - do_code_exe=do_code_exe - ) - - try: - code_text = self.decode_code_from_text(text) - return self.run(code_text, file_path) - except Exception as e: - return CodeBoxResponse( - code_exe_response=str(e), code_text=text, code_exe_type="error", code_exe_status=500, - do_code_exe=do_code_exe - ) - - async def achat(self, text: str, file_path: str = None, do_code_exe: bool = None) -> CodeBoxResponse: - do_code_exe = self.do_code_exe if do_code_exe is None else do_code_exe - if not do_code_exe: - return CodeBoxResponse( - code_exe_response=text, code_text=text, code_exe_type="text", code_exe_status=200, - do_code_exe=do_code_exe - ) - - try: - code_text = self.decode_code_from_text(text) - return await self.arun(code_text, file_path) - except Exception as e: - return CodeBoxResponse( - code_exe_response=str(e), code_text=text, code_exe_type="error", code_exe_status=500, - do_code_exe=do_code_exe - ) - - def run( - self, - code_text: str = None, - file_path: str = None, - retry=3, - ) -> CodeBoxResponse: - '''执行代码''' - pass - - async def arun( - self, - code_text: str = None, - file_path: str = None, - retry=3, - ) -> CodeBoxResponse: - '''执行代码''' - pass - - def decode_code_from_text(self, text): - pass - - def start(self,): - pass - - async def astart(self, ): - pass - - @abstractclassmethod - def stop(self) -> CodeBoxStatus: - """Terminate the CodeBox instance""" - - def __enter__(self, ) -> "BaseBox": - if not self.enter_status: - self.start() - return self - - async def __aenter__(self, ) -> "BaseBox": - if not self.enter_status: - await self.astart() - return self - - def __exit__(self, exc_type, exc_value, traceback) -> None: - self.stop() - diff --git a/coagent/sandbox/pycodebox.py b/coagent/sandbox/pycodebox.py deleted file mode 100644 index b1dc189..0000000 --- a/coagent/sandbox/pycodebox.py +++ /dev/null @@ -1,474 +0,0 @@ -import time, os, requests, json, uuid, subprocess, time, asyncio, aiohttp, re, traceback -import psutil -from typing import List, Optional -from loguru import logger - -from websocket import create_connection -from websockets.client import WebSocketClientProtocol, ClientConnection -from websockets.exceptions import ConnectionClosedError - -from coagent.base_configs.env_config import JUPYTER_WORK_PATH -from .basebox import BaseBox, CodeBoxResponse, CodeBoxStatus - - -class PyCodeBox(BaseBox): - - enter_status: bool = False - - def __init__( - self, - remote_url: str = "", - remote_ip: str = "http://127.0.0.1", - remote_port: str = "5050", - token: str = "mytoken", - jupyter_work_path: str = JUPYTER_WORK_PATH, - do_code_exe: bool = False, - do_remote: bool = False, - do_check_net: bool = True, - use_stop: bool = False - ): - super().__init__(remote_url, remote_ip, remote_port, token, do_code_exe, do_remote) - self.enter_status = True - self.do_check_net = do_check_net - self.jupyter_work_path = jupyter_work_path - # asyncio.run(self.astart()) - self.start() - - # logger.info(f"""remote_url: {self.remote_url}, - # remote_ip: {self.remote_ip}, - # remote_port: {self.remote_port}""") - - def decode_code_from_text(self, text: str) -> str: - pattern = r'```.*?```' - code_blocks = re.findall(pattern, text, re.DOTALL) - code_text: str = "\n".join([block.strip('`') for block in code_blocks]) - code_text = code_text[6:] if code_text.startswith("python") else code_text - code_text = code_text.replace("python\n", "").replace("code", "") - return code_text - - def run( - self, code_text: Optional[str] = None, - file_path: Optional[os.PathLike] = None, - retry = 3, - ) -> CodeBoxResponse: - if not code_text and not file_path: - return CodeBoxResponse( - code_exe_response="Code or file_path must be specifieds!", - code_text=code_text, - code_exe_type="text", - code_exe_status=502, - do_code_exe=self.do_code_exe, - ) - - if code_text and file_path: - return CodeBoxResponse( - code_exe_response="Can only specify code or the file to read_from!", - code_text=code_text, - code_exe_type="text", - code_exe_status=502, - do_code_exe=self.do_code_exe, - ) - - if file_path: - with open(file_path, "r", encoding="utf-8") as f: - code_text = f.read() - - # run code in jupyter kernel - if retry <= 0: - raise RuntimeError("Could not connect to kernel") - - if not self.ws: - raise RuntimeError("Jupyter not running. Make sure to start it first") - - # logger.debug(f"code_text: {len(code_text)}, {code_text}") - - self.ws.send( - json.dumps( - { - "header": { - "msg_id": (msg_id := uuid.uuid4().hex), - "msg_type": "execute_request", - }, - "parent_header": {}, - "metadata": {}, - "content": { - "code": code_text, - "silent": False, # True,则内核会执行代码,但不会发送执行结果(如输出) - "store_history": True, # True,则执行的代码会被记录在交互式环境的历史记录中 - "user_expressions": {}, - "allow_stdin": False, # True,允许代码执行时接受用户输入 - "stop_on_error": True, # True,当执行中遇到错误时,后续代码将不会继续执行。 - }, - "channel": "shell", - "buffers": [], - } - ) - ) - result = "" - while True: - try: - if isinstance(self.ws, WebSocketClientProtocol): - raise RuntimeError("Mixing asyncio and sync code is not supported") - received_msg = json.loads(self.ws.recv()) - except ConnectionClosedError: - # logger.debug("box start, ConnectionClosedError!!!") - self.start() - return self.run(code_text, file_path, retry - 1) - - if ( - received_msg["header"]["msg_type"] == "stream" - and received_msg["parent_header"]["msg_id"] == msg_id - ): - msg = received_msg["content"]["text"].strip() - if "Requirement already satisfied:" in msg: - continue - result += msg + "\n" - - elif ( - received_msg["header"]["msg_type"] == "execute_result" - and received_msg["parent_header"]["msg_id"] == msg_id - ): - result += received_msg["content"]["data"]["text/plain"].strip() + "\n" - - elif received_msg["header"]["msg_type"] == "display_data": - if "image/png" in received_msg["content"]["data"]: - return CodeBoxResponse( - code_exe_type="image/png", - code_text=code_text, - code_exe_response=received_msg["content"]["data"]["image/png"], - code_exe_status=200, - do_code_exe=self.do_code_exe - ) - if "text/plain" in received_msg["content"]["data"]: - return CodeBoxResponse( - code_exe_type="text", - code_text=code_text, - code_exe_response=received_msg["content"]["data"]["text/plain"], - code_exe_status=200, - do_code_exe=self.do_code_exe - ) - return CodeBoxResponse( - code_exe_type="error", - code_text=code_text, - code_exe_response=received_msg["content"]["data"]["text/plain"], - code_exe_status=420, - do_code_exe=self.do_code_exe - ) - elif ( - received_msg["header"]["msg_type"] == "status" - and received_msg["parent_header"]["msg_id"] == msg_id - and received_msg["content"]["execution_state"] == "idle" - ): - if len(result) > 500: - result = "[...]\n" + result[-500:] - return CodeBoxResponse( - code_exe_type="text", - code_text=code_text, - code_exe_response=result or "Code run successfully (no output)", - code_exe_status=200, - do_code_exe=self.do_code_exe - ) - - elif ( - received_msg["header"]["msg_type"] == "error" - and received_msg["parent_header"]["msg_id"] == msg_id - ): - error = ( - f"{received_msg['content']['ename']}: " - f"{received_msg['content']['evalue']}" - ) - return CodeBoxResponse( - code_exe_type="error", - code_text=code_text, - code_exe_response=error, - code_exe_status=500, - do_code_exe=self.do_code_exe - ) - - def _get_kernelid(self, ) -> None: - headers = {"Authorization": f'Token {self.token}', 'token': self.token} - response = requests.get(f"{self.kernel_url}?token={self.token}", headers=headers) - if len(response.json()) > 0: - self.kernel_id = response.json()[0]["id"] - else: - response = requests.post(f"{self.kernel_url}?token={self.token}", headers=headers) - self.kernel_id = response.json()["id"] - if self.kernel_id is None: - raise Exception("Could not start kernel") - - async def _aget_kernelid(self, ) -> None: - headers = {"Authorization": f'Token {self.token}', 'token': self.token} - # response = requests.get(f"{self.kernel_url}?token={self.token}", headers=headers) - async with aiohttp.ClientSession() as session: - async with session.get(f"{self.kernel_url}?token={self.token}", headers=headers, timeout=270) as resp: - if len(await resp.json()) > 0: - self.kernel_id = (await resp.json())[0]["id"] - else: - async with session.post(f"{self.kernel_url}?token={self.token}", headers=headers, timeout=270) as response: - self.kernel_id = (await response.json())["id"] - - # if len(response.json()) > 0: - # self.kernel_id = response.json()[0]["id"] - # else: - # response = requests.post(f"{self.kernel_url}?token={self.token}", headers=headers) - # self.kernel_id = response.json()["id"] - # if self.kernel_id is None: - # raise Exception("Could not start kernel") - def _check_connect(self, ) -> bool: - if self.kernel_url == "": - return False - - try: - response = requests.get(f"{self.kernel_url}?token={self.token}", timeout=10) - return response.status_code == 200 - except requests.exceptions.ConnectionError: - return False - except requests.exceptions.ReadTimeout: - return False - - async def _acheck_connect(self, ) -> bool: - if self.kernel_url == "": - return False - try: - async with aiohttp.ClientSession() as session: - async with session.get(f"{self.kernel_url}?token={self.token}", timeout=10) as resp: - return resp.status == 200 - except aiohttp.ClientConnectorError: - return False - except aiohttp.ServerDisconnectedError: - return False - - def _check_port(self, ) -> bool: - try: - response = requests.get(f"{self.remote_ip}:{self.remote_port}", timeout=10) - logger.warning(f"Port is conflict, please check your codebox's port {self.remote_port}") - return response.status_code == 200 - except requests.exceptions.ConnectionError: - return False - except requests.exceptions.ReadTimeout: - return False - - async def _acheck_port(self, ) -> bool: - try: - async with aiohttp.ClientSession() as session: - async with session.get(f"{self.remote_ip}:{self.remote_port}", timeout=10) as resp: - # logger.warning(f"Port is conflict, please check your codebox's port {self.remote_port}") - return resp.status == 200 - except aiohttp.ClientConnectorError: - return False - except aiohttp.ServerDisconnectedError: - return False - - def _check_connect_success(self, retry_nums: int = 2) -> bool: - if not self.do_check_net: return True - - while retry_nums > 0: - try: - connect_status = self._check_connect() - if connect_status: - # logger.info(f"{self.remote_url} connection success") - return True - except requests.exceptions.ConnectionError: - logger.info(f"{self.remote_url} connection fail") - retry_nums -= 1 - time.sleep(5) - raise BaseException(f"can't connect to {self.remote_url}") - - async def _acheck_connect_success(self, retry_nums: int = 2) -> bool: - if not self.do_check_net: return True - while retry_nums > 0: - try: - connect_status = await self._acheck_connect() - if connect_status: - # logger.info(f"{self.remote_url} connection success") - return True - except requests.exceptions.ConnectionError: - logger.info(f"{self.remote_url} connection fail") - retry_nums -= 1 - time.sleep(5) - raise BaseException(f"can't connect to {self.remote_url}") - - def start(self, ): - '''判断是从外部service执行还是内部启动notebook执行''' - self.jupyter = None - if self.do_remote: - # TODO自动检测日期,并重启容器 - self.kernel_url = self.remote_url + "/api/kernels" - self._check_connect_success() - - self._get_kernelid() - # logger.debug(self.kernel_url.replace("http", "ws") + f"/{self.kernel_id}/channels?token={self.token}") - self.wc_url = self.kernel_url.replace("http", "ws") + f"/{self.kernel_id}/channels?token={self.token}" - headers = {"Authorization": f'Token {self.token}', 'token': self.token} - self.ws = create_connection(self.wc_url, headers=headers) - else: - # TODO 自动检测本地接口 - port_status = self._check_port() - self.kernel_url = self.remote_url + "/api/kernels" - connect_status = self._check_connect() - if os.environ.get("log_verbose", "0") >= "2": - logger.info(f"port_status: {port_status}, connect_status: {connect_status}") - if port_status and not connect_status: - logger.error("Port is conflict, please check your codebox's port {self.remote_port}") - - if not connect_status: - self.jupyter = subprocess.Popen( - [ - "jupyter", "notebook", - f"--NotebookApp.token={self.token}", - f"--port={self.remote_port}", - "--no-browser", - "--ServerApp.disable_check_xsrf=True", - f"--notebook-dir={self.jupyter_work_path}" - ], - stderr=subprocess.PIPE, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - ) - - record = [] - while True and self.jupyter and len(record)<100: - line = self.jupyter.stderr.readline() - try: - content = line.decode("utf-8") - except: - content = line.decode("gbk") - # logger.debug(content) - record.append(content) - if "control-c" in content.lower(): - break - - self.kernel_url = self.remote_url + "/api/kernels" - self.do_check_net = True - self._check_connect_success() - self._get_kernelid() - self.wc_url = self.kernel_url.replace("http", "ws") + f"/{self.kernel_id}/channels?token={self.token}" - headers = {"Authorization": f'Token {self.token}', 'token': self.token} - retry_nums = 3 - while retry_nums>=0: - try: - self.ws = create_connection(self.wc_url, headers=headers, timeout=5) - break - except Exception as e: - logger.error(f"create ws connection timeout {e}") - retry_nums -= 1 - - async def astart(self, ): - '''判断是从外部service执行还是内部启动notebook执行''' - self.jupyter = None - if self.do_remote: - # TODO自动检测日期,并重启容器 - self.kernel_url = self.remote_url + "/api/kernels" - - await self._acheck_connect_success() - await self._aget_kernelid() - self.wc_url = self.kernel_url.replace("http", "ws") + f"/{self.kernel_id}/channels?token={self.token}" - headers = {"Authorization": f'Token {self.token}', 'token': self.token} - self.ws = create_connection(self.wc_url, headers=headers) - else: - # TODO 自动检测本地接口 - port_status = await self._acheck_port() - self.kernel_url = self.remote_url + "/api/kernels" - connect_status = await self._acheck_connect() - if os.environ.get("log_verbose", "0") >= "2": - logger.info(f"port_status: {port_status}, connect_status: {connect_status}") - if port_status and not connect_status: - raise BaseException(f"Port is conflict, please check your codebox's port {self.remote_port}") - - if not connect_status: - script_sh = [ - "jupyter", "notebook", - f"--NotebookApp.token={self.token}", - f"--port={self.remote_port}", - "--no-browser", - "--ServerApp.disable_check_xsrf=True", - f"--notebook-dir={self.jupyter_work_path}" - ] - self.jupyter = subprocess.Popen( - script_sh, - stderr=subprocess.PIPE, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - cwd=self.jupyter_work_path - ) - - record = [] - while True and self.jupyter and len(record)<100: - line = self.jupyter.stderr.readline() - try: - content = line.decode("utf-8") - except: - content = line.decode("gbk") - # logger.debug(content) - record.append(content) - if "control-c" in content.lower(): - break - self.kernel_url = self.remote_url + "/api/kernels" - self.do_check_net = True - await self._acheck_connect_success() - await self._aget_kernelid() - self.wc_url = self.kernel_url.replace("http", "ws") + f"/{self.kernel_id}/channels?token={self.token}" - headers = {"Authorization": f'Token {self.token}', 'token': self.token} - - retry_nums = 3 - while retry_nums>=0: - try: - self.ws = create_connection(self.wc_url, headers=headers, timeout=5) - break - except Exception as e: - logger.error(f"create ws connection timeout {e}") - retry_nums -= 1 - - def status(self,) -> CodeBoxStatus: - if not self.kernel_id: - self._get_kernelid() - - return CodeBoxStatus( - status="running" if self.kernel_id - and requests.get(self.kernel_url, timeout=270).status_code == 200 - else "stopped" - ) - - async def astatus(self,) -> CodeBoxStatus: - if not self.kernel_id: - await self._aget_kernelid() - - return CodeBoxStatus( - status="running" if self.kernel_id - and requests.get(self.kernel_url, timeout=270).status_code == 200 - else "stopped" - ) - - def restart(self, ) -> CodeBoxStatus: - return CodeBoxStatus(status="restared") - - def stop(self, ) -> CodeBoxStatus: - try: - if self.jupyter is not None: - for process in psutil.process_iter(["pid", "name", "cmdline"]): - # 检查进程名是否包含"jupyter" - if f'port={self.remote_port}' in str(process.info["cmdline"]).lower() and \ - "jupyter" in process.info['name'].lower(): - logger.warning(f'port={self.remote_port}, {process.info}') - # 关闭进程 - process.terminate() - - self.jupyter = None - except Exception as e: - logger.error(e) - - if self.ws is not None: - try: - if self.ws is not None: - self.ws.close() - else: - loop = asyncio.new_event_loop() - loop.run_until_complete(self.ws.close()) - except Exception as e: - logger.error(e) - self.ws = None - - # return CodeBoxStatus(status="stopped") - - def __del__(self): - self.stop() diff --git a/coagent/service/__init__.py b/coagent/service/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/coagent/service/api.py.bak b/coagent/service/api.py.bak deleted file mode 100644 index 921c606..0000000 --- a/coagent/service/api.py.bak +++ /dev/null @@ -1,210 +0,0 @@ -import nltk -import argparse -import uvicorn, os, sys -from fastapi.middleware.cors import CORSMiddleware -from starlette.responses import RedirectResponse -from typing import List - -src_dir = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -) -sys.path.append(src_dir) - -sys.path.append(os.path.dirname(os.path.dirname(__file__))) - -# from configs import VERSION -# from configs.model_config import NLTK_DATA_PATH -# from configs.server_config import OPEN_CROSS_DOMAIN - -from coagent.chat import LLMChat, SearchChat, KnowledgeChat -from coagent.service.kb_api import * -from coagent.service.cb_api import * -from coagent.utils.server_utils import BaseResponse, ListResponse, FastAPI, MakeFastAPIOffline - -# nltk.data.path = [NLTK_DATA_PATH] + nltk.data.path - -from coagent.chat import LLMChat, SearchChat, KnowledgeChat, CodeChat - -llmChat = LLMChat() -searchChat = SearchChat() -knowledgeChat = KnowledgeChat() -codeChat = CodeChat() - - -async def document(): - return RedirectResponse(url="/docs") - - -def create_app(): - app = FastAPI( - title="DevOps-ChatBot API Server", - version="v0.1.0" - # version=VERSION - ) - MakeFastAPIOffline(app) - # Add CORS middleware to allow all origins - # 在config.py中设置OPEN_DOMAIN=True,允许跨域 - # set OPEN_DOMAIN=True in config.py to allow cross-domain - if False: - # if OPEN_CROSS_DOMAIN: - app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], - ) - - app.get("/", - response_model=BaseResponse, - summary="swagger 文档")(document) - - # Tag: Chat - # app.post("/chat/fastchat", - # tags=["Chat"], - # summary="与llm模型对话(直接与fastchat api对话)")(openai_chat) - - app.post("/chat/chat", - tags=["Chat"], - summary="与llm模型对话(通过LLMChain)")(llmChat.chat) - - app.post("/chat/knowledge_base_chat", - tags=["Chat"], - summary="与知识库对话")(knowledgeChat.chat) - - app.post("/chat/search_engine_chat", - tags=["Chat"], - summary="与搜索引擎对话")(searchChat.chat) - - app.post("/chat/code_chat", - tags=["Chat"], - summary="与代码库对话")(codeChat.chat) - - # Tag: Knowledge Base Management - app.get("/knowledge_base/list_knowledge_bases", - tags=["Knowledge Base Management"], - response_model=ListResponse, - summary="获取知识库列表")(list_kbs) - - app.post("/knowledge_base/create_knowledge_base", - tags=["Knowledge Base Management"], - response_model=BaseResponse, - summary="创建知识库" - )(create_kb) - - app.post("/knowledge_base/delete_knowledge_base", - tags=["Knowledge Base Management"], - response_model=BaseResponse, - summary="删除知识库" - )(delete_kb) - - app.get("/knowledge_base/list_files", - tags=["Knowledge Base Management"], - response_model=ListResponse, - summary="获取知识库内的文件列表" - )(list_docs) - - app.post("/knowledge_base/search_docs", - tags=["Knowledge Base Management"], - response_model=List[DocumentWithScore], - summary="搜索知识库" - )(search_docs) - - app.post("/knowledge_base/upload_docs", - tags=["Knowledge Base Management"], - response_model=BaseResponse, - summary="上传文件到知识库,并/或进行向量化" - )(upload_doc) - - app.post("/knowledge_base/delete_docs", - tags=["Knowledge Base Management"], - response_model=BaseResponse, - summary="删除知识库内指定文件" - )(delete_doc) - - app.post("/knowledge_base/update_docs", - tags=["Knowledge Base Management"], - response_model=BaseResponse, - summary="更新现有文件到知识库" - )(update_doc) - - app.get("/knowledge_base/download_doc", - tags=["Knowledge Base Management"], - summary="下载对应的知识文件")(download_doc) - - app.post("/knowledge_base/recreate_vector_store", - tags=["Knowledge Base Management"], - summary="根据content中文档重建向量库,流式输出处理进度。" - )(recreate_vector_store) - - app.post("/code_base/create_code_base", - tags=["Code Base Management"], - summary="新建 code_base" - )(create_cb) - - app.post("/code_base/delete_code_base", - tags=["Code Base Management"], - summary="删除 code_base" - )(delete_cb) - - app.post("/code_base/code_base_chat", - tags=["Code Base Management"], - summary="删除 code_base" - )(delete_cb) - - app.get("/code_base/list_code_bases", - tags=["Code Base Management"], - summary="列举 code_base", - response_model=ListResponse - )(list_cbs) - - # # LLM模型相关接口 - # app.post("/llm_model/list_models", - # tags=["LLM Model Management"], - # summary="列出当前已加载的模型", - # )(list_llm_models) - - # app.post("/llm_model/stop", - # tags=["LLM Model Management"], - # summary="停止指定的LLM模型(Model Worker)", - # )(stop_llm_model) - - # app.post("/llm_model/change", - # tags=["LLM Model Management"], - # summary="切换指定的LLM模型(Model Worker)", - # )(change_llm_model) - - return app - - -app = create_app() - - -def run_api(host, port, **kwargs): - if kwargs.get("ssl_keyfile") and kwargs.get("ssl_certfile"): - uvicorn.run(app, - host=host, - port=port, - ssl_keyfile=kwargs.get("ssl_keyfile"), - ssl_certfile=kwargs.get("ssl_certfile"), - ) - else: - uvicorn.run(app, host=host, port=port) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(prog='DevOps-ChatBot', - description='About DevOps-ChatBot, local knowledge based LLM with langchain' - ' | 基于本地知识库的 LLM 问答') - parser.add_argument("--host", type=str, default="0.0.0.0") - parser.add_argument("--port", type=int, default=7861) - parser.add_argument("--ssl_keyfile", type=str) - parser.add_argument("--ssl_certfile", type=str) - # 初始化消息 - args = parser.parse_args() - args_dict = vars(args) - run_api(host=args.host, - port=args.port, - ssl_keyfile=args.ssl_keyfile, - ssl_certfile=args.ssl_certfile, - ) diff --git a/coagent/service/base_service.py b/coagent/service/base_service.py deleted file mode 100644 index f5000a5..0000000 --- a/coagent/service/base_service.py +++ /dev/null @@ -1,203 +0,0 @@ -from abc import ABC, abstractmethod -from typing import List -import os - -from langchain.embeddings.base import Embeddings -from langchain.docstore.document import Document - -# from configs.model_config import ( -# kbs_config, VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD, -# EMBEDDING_MODEL, EMBEDDING_DEVICE, KB_ROOT_PATH -# ) -# from configs.model_config import embedding_model_dict -from coagent.base_configs.env_config import ( - VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD, kbs_config -) -from coagent.orm.commands import * -from coagent.utils.path_utils import * -from coagent.orm.utils import DocumentFile -from coagent.embeddings.utils import load_embeddings, load_embeddings_from_path -from coagent.retrieval.text_splitter import LCTextSplitter -from coagent.llm_models.llm_config import EmbedConfig - - -class SupportedVSType: - FAISS = 'faiss' - # MILVUS = 'milvus' - # DEFAULT = 'default' - # PG = 'pg' - - -class KBService(ABC): - - def __init__(self, - knowledge_base_name: str, - embed_config: EmbedConfig, - # embed_model: str = "text2vec-base-chinese", - kb_root_path: str, - ): - self.kb_name = knowledge_base_name - # self.embed_model = embed_model - self.embed_config = embed_config - self.kb_root_path = kb_root_path - self.kb_path = get_kb_path(self.kb_name, kb_root_path) - self.doc_path = get_doc_path(self.kb_name, kb_root_path) - self.do_init() - - def _load_embeddings(self) -> Embeddings: - # return load_embeddings(self.embed_model, embed_device, embedding_model_dict) - return load_embeddings_from_path(self.embed_config.embed_model_path, self.embed_config.model_device, self.embed_config.langchain_embeddings) - - def create_kb(self): - """ - 创建知识库 - """ - if not os.path.exists(self.doc_path): - os.makedirs(self.doc_path) - self.do_create_kb() - status = add_kb_to_db(self.kb_name, self.vs_type(), self.embed_config.embed_model) - return status - - def clear_vs(self): - """ - 删除向量库中所有内容 - """ - self.do_clear_vs() - status = delete_files_from_db(self.kb_name) - return status - - def drop_kb(self): - """ - 删除知识库 - """ - self.do_drop_kb() - status = delete_kb_from_db(self.kb_name) - return status - - def add_doc(self, kb_file: DocumentFile, **kwargs): - """ - 向知识库添加文件 - """ - lctTextSplitter = LCTextSplitter(kb_file.filepath) - docs = lctTextSplitter.file2text() - if docs: - self.delete_doc(kb_file) - embeddings = self._load_embeddings() - self.do_add_doc(docs, embeddings, **kwargs) - status = add_doc_to_db(kb_file) - else: - status = False - return status - - def delete_doc(self, kb_file: DocumentFile, delete_content: bool = False, **kwargs): - """ - 从知识库删除文件 - """ - self.do_delete_doc(kb_file, **kwargs) - status = delete_file_from_db(kb_file) - if delete_content and os.path.exists(kb_file.filepath): - os.remove(kb_file.filepath) - return status - - def update_doc(self, kb_file: DocumentFile, **kwargs): - """ - 使用content中的文件更新向量库 - """ - if os.path.exists(kb_file.filepath): - self.delete_doc(kb_file, **kwargs) - return self.add_doc(kb_file, **kwargs) - - def exist_doc(self, file_name: str): - return doc_exists(DocumentFile(knowledge_base_name=self.kb_name, - filename=file_name, kb_root_path=self.kb_root_path)) - - def list_docs(self): - return list_docs_from_db(self.kb_name) - - def search_docs(self, - query: str, - top_k: int = VECTOR_SEARCH_TOP_K, - score_threshold: float = SCORE_THRESHOLD, - ): - embeddings = self._load_embeddings() - docs = self.do_search(query, top_k, score_threshold, embeddings) - return docs - - @abstractmethod - def do_create_kb(self): - """ - 创建知识库子类实自己逻辑 - """ - pass - - @staticmethod - def list_kbs_type(): - return list(kbs_config.keys()) - - @classmethod - def list_kbs(cls): - return list_kbs_from_db() - - def exists(self, kb_name: str = None): - kb_name = kb_name or self.kb_name - return kb_exists(kb_name) - - @abstractmethod - def vs_type(self) -> str: - pass - - @abstractmethod - def do_init(self): - pass - - @abstractmethod - def do_drop_kb(self): - """ - 删除知识库子类实自己逻辑 - """ - pass - - @abstractmethod - def do_search(self, - query: str, - top_k: int, - embeddings: Embeddings, - ) -> List[Document]: - """ - 搜索知识库子类实自己逻辑 - """ - pass - - @abstractmethod - def do_add_doc(self, - docs: List[Document], - embeddings: Embeddings, - ): - """ - 向知识库添加文档子类实自己逻辑 - """ - pass - - @abstractmethod - def get_all_documents(self, - embeddings: Embeddings, - ): - """ - 获取知识库所有文档 - """ - pass - - @abstractmethod - def do_delete_doc(self, - kb_file: DocumentFile): - """ - 从知识库删除文档子类实自己逻辑 - """ - pass - - @abstractmethod - def do_clear_vs(self): - """ - 从知识库删除全部向量子类实自己逻辑 - """ - pass diff --git a/coagent/service/cb_api.py b/coagent/service/cb_api.py deleted file mode 100644 index 5705644..0000000 --- a/coagent/service/cb_api.py +++ /dev/null @@ -1,255 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: cb_api.py -@time: 2023/10/23 下午7:08 -@desc: -''' - -import urllib, os, json, traceback -from typing import List, Dict -import shutil - -from fastapi.responses import StreamingResponse, FileResponse -from fastapi import File, Form, Body, Query, UploadFile -from langchain.docstore.document import Document - -from .service_factory import KBServiceFactory -from coagent.utils.server_utils import BaseResponse, ListResponse -from coagent.utils.path_utils import * -from coagent.orm.commands import * -from coagent.db_handler.graph_db_handler.nebula_handler import NebulaHandler -from coagent.db_handler.vector_db_handler.chroma_handler import ChromaHandler -from coagent.base_configs.env_config import ( - CB_ROOT_PATH, - NEBULA_HOST, NEBULA_PORT, NEBULA_USER, NEBULA_PASSWORD, NEBULA_STORAGED_PORT, - CHROMA_PERSISTENT_PATH -) - - -# from configs.model_config import ( -# CB_ROOT_PATH -# ) - -# from coagent.codebase_handler.codebase_handler import CodeBaseHandler -from coagent.llm_models.llm_config import EmbedConfig, LLMConfig -from coagent.codechat.codebase_handler.codebase_handler import CodeBaseHandler - -from loguru import logger - - -async def list_cbs(): - # Get List of Knowledge Base - return ListResponse(data=list_cbs_from_db()) - - -async def create_cb(zip_file, - cb_name: str = Body(..., examples=["samples"]), - code_path: str = Body(..., examples=["samples"]), - do_interpret: bool = Body(..., examples=["samples"]), - api_key: bool = Body(..., examples=["samples"]), - api_base_url: bool = Body(..., examples=["samples"]), - embed_model: bool = Body(..., examples=["samples"]), - embed_model_path: bool = Body(..., examples=["samples"]), - embed_engine: bool = Body(..., examples=["samples"]), - model_name: bool = Body(..., examples=["samples"]), - temperature: bool = Body(..., examples=["samples"]), - model_device: bool = Body(..., examples=["samples"]), - embed_config: EmbedConfig = None, - ) -> BaseResponse: - logger.info('cb_name={}, zip_path={}, do_interpret={}'.format(cb_name, code_path, do_interpret)) - - embed_config: EmbedConfig = EmbedConfig(**locals()) if embed_config is None else embed_config - llm_config: LLMConfig = LLMConfig(**locals()) - - # Create selected knowledge base - if not validate_kb_name(cb_name): - return BaseResponse(code=403, msg="Don't attack me") - if cb_name is None or cb_name.strip() == "": - return BaseResponse(code=404, msg="知识库名称不能为空,请重新填写知识库名称") - - cb = cb_exists(cb_name) - if cb: - return BaseResponse(code=404, msg=f"已存在同名代码知识库 {cb_name}") - - try: - logger.info('start build code base') - cbh = CodeBaseHandler(cb_name, code_path, embed_config=embed_config, llm_config=llm_config) - vertices_num, edge_num, file_num = cbh.import_code(zip_file=zip_file, do_interpret=do_interpret) - logger.info('build code base done') - - # create cb to table - add_cb_to_db(cb_name, cbh.code_path, vertices_num, file_num, do_interpret) - logger.info('add cb to mysql table success') - except Exception as e: - print(e) - logger.exception(e) - return BaseResponse(code=500, msg=f"创建代码知识库出错: {e}") - - return BaseResponse(code=200, msg=f"已新增代码知识库 {cb_name}") - - -async def delete_cb( - cb_name: str = Body(..., examples=["samples"]), - api_key: bool = Body(..., examples=["samples"]), - api_base_url: bool = Body(..., examples=["samples"]), - embed_model: bool = Body(..., examples=["samples"]), - embed_model_path: bool = Body(..., examples=["samples"]), - embed_engine: bool = Body(..., examples=["samples"]), - model_name: bool = Body(..., examples=["samples"]), - temperature: bool = Body(..., examples=["samples"]), - model_device: bool = Body(..., examples=["samples"]), - embed_config: EmbedConfig = None, - ) -> BaseResponse: - logger.info('cb_name={}'.format(cb_name)) - embed_config: EmbedConfig = EmbedConfig(**locals()) if embed_config is None else embed_config - llm_config: LLMConfig = LLMConfig(**locals()) - # Create selected knowledge base - if not validate_kb_name(cb_name): - return BaseResponse(code=403, msg="Don't attack me") - if cb_name is None or cb_name.strip() == "": - return BaseResponse(code=404, msg="知识库名称不能为空,请重新填写知识库名称") - - cb = cb_exists(cb_name) - if cb: - try: - delete_cb_from_db(cb_name) - - # delete local file - shutil.rmtree(CB_ROOT_PATH + os.sep + cb_name) - - # delete from codebase - cbh = CodeBaseHandler(cb_name, embed_config=embed_config, llm_config=llm_config) - cbh.delete_codebase(codebase_name=cb_name) - - except Exception as e: - print(e) - return BaseResponse(code=500, msg=f"删除代码知识库出错: {e}") - - return BaseResponse(code=200, msg=f"已删除代码知识库 {cb_name}") - - -def search_code(cb_name: str = Body(..., examples=["sofaboot"]), - query: str = Body(..., examples=['你好']), - code_limit: int = Body(..., examples=['1']), - search_type: str = Body(..., examples=['你好']), - history_node_list: list = Body(...), - api_key: bool = Body(..., examples=["samples"]), - api_base_url: bool = Body(..., examples=["samples"]), - embed_model: bool = Body(..., examples=["samples"]), - embed_model_path: bool = Body(..., examples=["samples"]), - embed_engine: bool = Body(..., examples=["samples"]), - model_name: bool = Body(..., examples=["samples"]), - temperature: bool = Body(..., examples=["samples"]), - model_device: bool = Body(..., examples=["samples"]), - use_nh: bool = True, - local_graph_path: str = '', - embed_config: EmbedConfig = None, - ) -> dict: - - if os.environ.get("log_verbose", "0") >= "2": - logger.info(f'local_graph_path={local_graph_path}') - logger.info('cb_name={}'.format(cb_name)) - logger.info('query={}'.format(query)) - logger.info('code_limit={}'.format(code_limit)) - logger.info('search_type={}'.format(search_type)) - logger.info('history_node_list={}'.format(history_node_list)) - embed_config: EmbedConfig = EmbedConfig(**locals()) if embed_config is None else embed_config - llm_config: LLMConfig = LLMConfig(**locals()) - try: - # load codebase - cbh = CodeBaseHandler(codebase_name=cb_name, embed_config=embed_config, llm_config=llm_config, - use_nh=use_nh, local_graph_path=local_graph_path) - - # search code - context, related_vertices = cbh.search_code(query, search_type=search_type, limit=code_limit) - - res = { - 'context': context, - 'related_vertices': related_vertices - } - return res - except Exception as e: - logger.exception(e) - return {} - - -def search_related_vertices(cb_name: str = Body(..., examples=["sofaboot"]), - vertex: str = Body(..., examples=['***'])) -> dict: - - logger.info('cb_name={}'.format(cb_name)) - logger.info('vertex={}'.format(vertex)) - - try: - # load codebase - nh = NebulaHandler(host=NEBULA_HOST, port=NEBULA_PORT, username=NEBULA_USER, - password=NEBULA_PASSWORD, space_name=cb_name) - - if vertex.endswith(".java"): - cypher = f'''MATCH (v1)--(v2:package) WHERE id(v1) == '{vertex}' RETURN id(v2) as id;''' - else: - cypher = f'''MATCH (v1)--(v2) WHERE id(v1) == '{vertex}' RETURN id(v2) as id;''' - # cypher = f'''MATCH (v1)--(v2) WHERE id(v1) == '{vertex}' RETURN v2;''' - cypher_res = nh.execute_cypher(cypher=cypher, format_res=True) - related_vertices = cypher_res.get('id', []) - related_vertices = [i.as_string() for i in related_vertices] - - res = { - 'vertices': related_vertices - } - - return res - except Exception as e: - logger.exception(e) - return {} - - -def search_code_by_vertex(cb_name: str = Body(..., examples=["sofaboot"]), - vertex: str = Body(..., examples=['***'])) -> dict: - - # logger.info('cb_name={}'.format(cb_name)) - # logger.info('vertex={}'.format(vertex)) - - try: - nh = NebulaHandler(host=NEBULA_HOST, port=NEBULA_PORT, username=NEBULA_USER, - password=NEBULA_PASSWORD, space_name=cb_name) - - cypher = f'''MATCH (v1:package)-[e:contain]->(v2) WHERE id(v2) == '{vertex}' RETURN id(v1) as id;''' - cypher_res = nh.execute_cypher(cypher=cypher, format_res=True) - - related_vertices = cypher_res.get('id', []) - related_vertices = [i.as_string() for i in related_vertices] - - if not related_vertices: - return {'code': ''} - ch = ChromaHandler(path=CHROMA_PERSISTENT_PATH, collection_name=cb_name) - - # logger.info(related_vertices) - chroma_res = ch.get(ids=related_vertices, include=['metadatas']) - # logger.info(chroma_res) - - if chroma_res['result']['ids']: - code_text = chroma_res['result']['metadatas'][0]['code_text'] - else: - code_text = '' - - res = { - 'code': code_text - } - - return res - except Exception as e: - logger.exception(e) - return {'code': ""} - - -def cb_exists_api(cb_name: str = Body(..., examples=["sofaboot"])) -> bool: - try: - res = cb_exists(cb_name) - return res - except Exception as e: - logger.exception(e) - return False - - - diff --git a/coagent/service/faiss_db_service.py b/coagent/service/faiss_db_service.py deleted file mode 100644 index c7eac21..0000000 --- a/coagent/service/faiss_db_service.py +++ /dev/null @@ -1,181 +0,0 @@ -import os -import shutil -from typing import List -from functools import lru_cache -from loguru import logger - -# from langchain.vectorstores import FAISS -from langchain.embeddings.base import Embeddings -from langchain.docstore.document import Document -from langchain.embeddings.huggingface import HuggingFaceEmbeddings - -from coagent.base_configs.env_config import ( - KB_ROOT_PATH, - CACHED_VS_NUM, SCORE_THRESHOLD, FAISS_NORMALIZE_L2 -) - -from .base_service import KBService, SupportedVSType -from coagent.utils.path_utils import * -from coagent.orm.utils import DocumentFile -from coagent.utils.server_utils import torch_gc -from coagent.embeddings.utils import load_embeddings, load_embeddings_from_path -from coagent.embeddings.faiss_m import FAISS -from coagent.llm_models.llm_config import EmbedConfig - - -# make HuggingFaceEmbeddings hashable -def _embeddings_hash(self): - return hash(self.model_name) - - -HuggingFaceEmbeddings.__hash__ = _embeddings_hash - -_VECTOR_STORE_TICKS = {} - - -# @lru_cache(CACHED_VS_NUM) -def load_vector_store( - knowledge_base_name: str, - embed_config: EmbedConfig, - embeddings: Embeddings = None, - tick: int = 0, # tick will be changed by upload_doc etc. and make cache refreshed. - kb_root_path: str = KB_ROOT_PATH, -): - # print(f"loading vector store in '{knowledge_base_name}'.") - vs_path = get_vs_path(knowledge_base_name, kb_root_path) - if embeddings is None: - embeddings = load_embeddings_from_path(embed_config.embed_model_path, embed_config.model_device, embed_config.langchain_embeddings) - - if not os.path.exists(vs_path): - os.makedirs(vs_path) - - distance_strategy = "EUCLIDEAN_DISTANCE" - if "index.faiss" in os.listdir(vs_path): - search_index = FAISS.load_local(vs_path, embeddings, normalize_L2=FAISS_NORMALIZE_L2, distance_strategy=distance_strategy) - else: - # create an empty vector store - doc = Document(page_content="init", metadata={}) - search_index = FAISS.from_documents([doc], embeddings, normalize_L2=FAISS_NORMALIZE_L2, distance_strategy=distance_strategy) - ids = [k for k, v in search_index.docstore._dict.items()] - search_index.delete(ids) - search_index.save_local(vs_path) - - if tick == 0: # vector store is loaded first time - _VECTOR_STORE_TICKS[knowledge_base_name] = 0 - - # search_index.embedding_function = embeddings.embed_documents - return search_index - - -def refresh_vs_cache(kb_name: str): - """ - make vector store cache refreshed when next loading - """ - _VECTOR_STORE_TICKS[kb_name] = _VECTOR_STORE_TICKS.get(kb_name, 0) + 1 - print(f"知识库 {kb_name} 缓存刷新:{_VECTOR_STORE_TICKS[kb_name]}") - - -class FaissKBService(KBService): - vs_path: str - kb_path: str - - def vs_type(self) -> str: - return SupportedVSType.FAISS - - @staticmethod - def get_vs_path(knowledge_base_name: str): - return os.path.join(FaissKBService.get_kb_path(knowledge_base_name), "vector_store") - - @staticmethod - def get_kb_path(knowledge_base_name: str): - return os.path.join(KB_ROOT_PATH, knowledge_base_name) - - def do_init(self): - self.kb_path = FaissKBService.get_kb_path(self.kb_name) - self.vs_path = FaissKBService.get_vs_path(self.kb_name) - - def do_create_kb(self): - if not os.path.exists(self.vs_path): - os.makedirs(self.vs_path) - load_vector_store(self.kb_name, self.embed_config) - - def do_drop_kb(self): - self.clear_vs() - shutil.rmtree(self.kb_path) - - def do_search(self, - query: str, - top_k: int, - score_threshold: float = SCORE_THRESHOLD, - embeddings: Embeddings = None, - ) -> List[Document]: - search_index = load_vector_store(self.kb_name, - self.embed_config, - embeddings=embeddings, - tick=_VECTOR_STORE_TICKS.get(self.kb_name), - kb_root_path=self.kb_root_path) - docs = search_index.similarity_search_with_score(query, k=top_k, score_threshold=score_threshold) - return docs - - def get_all_documents(self, embeddings: Embeddings = None,): - search_index = load_vector_store(self.kb_name, - self.embed_config, - embeddings=embeddings, - tick=_VECTOR_STORE_TICKS.get(self.kb_name), - kb_root_path=self.kb_root_path) - return search_index.get_all_documents() - - def do_add_doc(self, - docs: List[Document], - embeddings: Embeddings, - **kwargs, - ): - vector_store = load_vector_store(self.kb_name, - self.embed_config, - embeddings=embeddings, - tick=_VECTOR_STORE_TICKS.get(self.kb_name, 0), - kb_root_path=self.kb_root_path) - vector_store.embedding_function = embeddings.embed_documents - logger.info("loaded docs, docs' lens is {}".format(len(docs))) - vector_store.add_documents(docs) - torch_gc() - if not kwargs.get("not_refresh_vs_cache"): - vector_store.save_local(self.vs_path) - refresh_vs_cache(self.kb_name) - - def do_delete_doc(self, - kb_file: DocumentFile, - **kwargs): - embeddings = self._load_embeddings() - vector_store = load_vector_store(self.kb_name, - self.embed_config, - embeddings=embeddings, - tick=_VECTOR_STORE_TICKS.get(self.kb_name, 0), - kb_root_path=self.kb_root_path) - - ids = [k for k, v in vector_store.docstore._dict.items() if v.metadata["source"] == kb_file.filepath] - if len(ids) == 0: - return None - - vector_store.delete(ids) - if not kwargs.get("not_refresh_vs_cache"): - vector_store.save_local(self.vs_path) - refresh_vs_cache(self.kb_name) - - return True - - def do_clear_vs(self): - if os.path.exists(self.vs_path): - shutil.rmtree(self.vs_path) - os.makedirs(self.vs_path) - refresh_vs_cache(self.kb_name) - - def exist_doc(self, file_name: str): - if super().exist_doc(file_name): - return "in_db" - - content_path = os.path.join(self.kb_path, "content") - if os.path.isfile(os.path.join(content_path, file_name)): - return "in_folder" - else: - return False diff --git a/coagent/service/kb_api.py b/coagent/service/kb_api.py deleted file mode 100644 index 1607e65..0000000 --- a/coagent/service/kb_api.py +++ /dev/null @@ -1,332 +0,0 @@ -import urllib, os, json, traceback -from typing import List, Dict -from loguru import logger - -from fastapi.responses import StreamingResponse, FileResponse -from fastapi import Body, File, Form, Body, Query, UploadFile -from langchain.docstore.document import Document - -from .service_factory import KBServiceFactory -from coagent.utils.server_utils import BaseResponse, ListResponse -from coagent.utils.path_utils import * -from coagent.orm.commands import * -from coagent.orm.utils import DocumentFile -# from configs.model_config import ( -# DEFAULT_VS_TYPE, EMBEDDING_MODEL, VECTOR_SEARCH_TOP_K, SCORE_THRESHOLD, KB_ROOT_PATH -# ) -from coagent.llm_models.llm_config import EmbedConfig - - -async def list_kbs(): - # Get List of Knowledge Base - return ListResponse(data=list_kbs_from_db()) - - -async def create_kb(knowledge_base_name: str = Body(..., examples=["samples"]), - vector_store_type: str = Body("faiss"), - kb_root_path: str =Body(""), - api_key: bool = Body(..., examples=["samples"]), - api_base_url: bool = Body(..., examples=["samples"]), - embed_model: bool = Body(..., examples=["samples"]), - embed_model_path: bool = Body(..., examples=["samples"]), - model_device: bool = Body(..., examples=["samples"]), - embed_engine: bool = Body(..., examples=["samples"]), - ) -> BaseResponse: - embed_config: EmbedConfig = EmbedConfig(**locals()) - # Create selected knowledge base - if not validate_kb_name(knowledge_base_name): - return BaseResponse(code=403, msg="Don't attack me") - if knowledge_base_name is None or knowledge_base_name.strip() == "": - return BaseResponse(code=404, msg="知识库名称不能为空,请重新填写知识库名称") - - kb = KBServiceFactory.get_service_by_name(knowledge_base_name, embed_config, kb_root_path) - if kb is not None: - return BaseResponse(code=404, msg=f"已存在同名知识库 {knowledge_base_name}") - - kb = KBServiceFactory.get_service(knowledge_base_name, vector_store_type, embed_config, kb_root_path) - try: - kb.create_kb() - except Exception as e: - logger.exception(e) - return BaseResponse(code=500, msg=f"创建知识库出错: {e}") - - return BaseResponse(code=200, msg=f"已新增知识库 {knowledge_base_name}") - - -async def delete_kb( - knowledge_base_name: str = Body(..., examples=["samples"]), - kb_root_path: str =Body(""), - ) -> BaseResponse: - # Delete selected knowledge base - if not validate_kb_name(knowledge_base_name): - return BaseResponse(code=403, msg="Don't attack me") - knowledge_base_name = urllib.parse.unquote(knowledge_base_name) - - kb = KBServiceFactory.get_service_by_name(knowledge_base_name, None, kb_root_path) - - if kb is None: - return BaseResponse(code=404, msg=f"未找到知识库 {knowledge_base_name}") - - try: - status = kb.clear_vs() - status = kb.drop_kb() - if status: - return BaseResponse(code=200, msg=f"成功删除知识库 {knowledge_base_name}") - except Exception as e: - print(e) - return BaseResponse(code=500, msg=f"删除知识库时出现意外: {e}") - - return BaseResponse(code=500, msg=f"删除知识库失败 {knowledge_base_name}") - - - -class DocumentWithScore(Document): - score: float = None - - -def search_docs(query: str = Body(..., description="用户输入", examples=["你好"]), - knowledge_base_name: str = Body(..., description="知识库名称", examples=["samples"]), - top_k: int = Body(5, description="匹配向量数"), - score_threshold: float = Body(1.0, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1), - kb_root_path: str =Body(""), - api_key: bool = Body(..., examples=["samples"]), - api_base_url: bool = Body(..., examples=["samples"]), - embed_model: bool = Body(..., examples=["samples"]), - embed_model_path: bool = Body(..., examples=["samples"]), - model_device: bool = Body(..., examples=["samples"]), - embed_engine: bool = Body(..., examples=["samples"]), - ) -> List[DocumentWithScore]: - - embed_config: EmbedConfig = EmbedConfig(**locals()) - kb = KBServiceFactory.get_service_by_name(knowledge_base_name, embed_config, kb_root_path) - if kb is None: - return [] - docs = kb.search_docs(query, top_k, score_threshold) - data = [DocumentWithScore(**x[0].dict(), score=x[1]) for x in docs] - - return data - - -async def list_docs( - knowledge_base_name: str, - kb_root_path: str =Body(""), -) -> ListResponse: - if not validate_kb_name(knowledge_base_name): - return ListResponse(code=403, msg="Don't attack me", data=[]) - - knowledge_base_name = urllib.parse.unquote(knowledge_base_name) - kb = KBServiceFactory.get_service_by_name(knowledge_base_name, None, kb_root_path) - if kb is None: - return ListResponse(code=404, msg=f"未找到知识库 {knowledge_base_name}", data=[]) - else: - all_doc_names = kb.list_docs() - return ListResponse(data=all_doc_names) - - -async def upload_doc(file: UploadFile = File(..., description="上传文件"), - knowledge_base_name: str = Form(..., description="知识库名称", examples=["kb1"]), - override: bool = Form(False, description="覆盖已有文件"), - not_refresh_vs_cache: bool = Form(False, description="暂不保存向量库(用于FAISS)"), - kb_root_path: str =Body(""), - api_key: bool = Body(..., examples=["samples"]), - api_base_url: bool = Body(..., examples=["samples"]), - embed_model: bool = Body(..., examples=["samples"]), - embed_model_path: bool = Body(..., examples=["samples"]), - model_device: bool = Body(..., examples=["samples"]), - embed_engine: bool = Body(..., examples=["samples"]), - ) -> BaseResponse: - if not validate_kb_name(knowledge_base_name): - return BaseResponse(code=403, msg="Don't attack me") - - embed_config: EmbedConfig = EmbedConfig(**locals()) - kb = KBServiceFactory.get_service_by_name(knowledge_base_name, embed_config, kb_root_path) - if kb is None: - return BaseResponse(code=404, msg=f"未找到知识库 {knowledge_base_name}") - - file_content = await file.read() # 读取上传文件的内容 - - try: - kb_file = DocumentFile(filename=file.filename, - knowledge_base_name=knowledge_base_name, - kb_root_path=kb_root_path - ) - - if (os.path.exists(kb_file.filepath) - and not override - and os.path.getsize(kb_file.filepath) == len(file_content) - ): - # TODO: filesize 不同后的处理 - file_status = f"文件 {kb_file.filename} 已存在。" - return BaseResponse(code=404, msg=file_status) - - with open(kb_file.filepath, "wb") as f: - f.write(file_content) - except Exception as e: - logger.error(traceback.format_exc()) - return BaseResponse(code=500, msg=f"{kb_file.filename} 文件上传失败,报错信息为: {e}") - - try: - kb.add_doc(kb_file, not_refresh_vs_cache=not_refresh_vs_cache) - except Exception as e: - logger.error(traceback.format_exc()) - return BaseResponse(code=500, msg=f"{kb_file.filename} 文件向量化失败,报错信息为: {e}") - - return BaseResponse(code=200, msg=f"成功上传文件 {kb_file.filename}") - - -async def delete_doc(knowledge_base_name: str = Body(..., examples=["samples"]), - doc_name: str = Body(..., examples=["file_name.md"]), - delete_content: bool = Body(False), - not_refresh_vs_cache: bool = Body(False, description="暂不保存向量库(用于FAISS)"), - kb_root_path: str =Body(""), - api_key: bool = Body(..., examples=["samples"]), - api_base_url: bool = Body(..., examples=["samples"]), - embed_model: bool = Body(..., examples=["samples"]), - embed_model_path: bool = Body(..., examples=["samples"]), - model_device: bool = Body(..., examples=["samples"]), - embed_engine: bool = Body(..., examples=["samples"]), - ) -> BaseResponse: - if not validate_kb_name(knowledge_base_name): - return BaseResponse(code=403, msg="Don't attack me") - - embed_config: EmbedConfig = EmbedConfig(**locals()) - knowledge_base_name = urllib.parse.unquote(knowledge_base_name) - kb = KBServiceFactory.get_service_by_name(knowledge_base_name, embed_config, kb_root_path) - if kb is None: - return BaseResponse(code=404, msg=f"未找到知识库 {knowledge_base_name}") - - if not kb.exist_doc(doc_name): - return BaseResponse(code=404, msg=f"未找到文件 {doc_name}") - - try: - kb_file = DocumentFile(filename=doc_name, - knowledge_base_name=knowledge_base_name, - kb_root_path=kb_root_path) - kb.delete_doc(kb_file, delete_content, not_refresh_vs_cache=not_refresh_vs_cache) - except Exception as e: - logger.exception(e) - return BaseResponse(code=500, msg=f"{kb_file.filename} 文件删除失败,错误信息:{e}") - - return BaseResponse(code=200, msg=f"{kb_file.filename} 文件删除成功") - - -async def update_doc( - knowledge_base_name: str = Body(..., examples=["samples"]), - file_name: str = Body(..., examples=["file_name"]), - not_refresh_vs_cache: bool = Body(False, description="暂不保存向量库(用于FAISS)"), - kb_root_path: str =Body(""), - api_key: bool = Body(..., examples=["samples"]), - api_base_url: bool = Body(..., examples=["samples"]), - embed_model: bool = Body(..., examples=["samples"]), - embed_model_path: bool = Body(..., examples=["samples"]), - model_device: bool = Body(..., examples=["samples"]), - embed_engine: bool = Body(..., examples=["samples"]), - ) -> BaseResponse: - ''' - 更新知识库文档 - ''' - embed_config: EmbedConfig = EmbedConfig(**locals()) - if not validate_kb_name(knowledge_base_name): - return BaseResponse(code=403, msg="Don't attack me") - - kb = KBServiceFactory.get_service_by_name(knowledge_base_name, embed_config, kb_root_path) - if kb is None: - return BaseResponse(code=404, msg=f"未找到知识库 {knowledge_base_name}") - - try: - kb_file = DocumentFile(filename=file_name, - knowledge_base_name=knowledge_base_name, - kb_root_path=kb_root_path) - if os.path.exists(kb_file.filepath): - kb.update_doc(kb_file, not_refresh_vs_cache=not_refresh_vs_cache) - return BaseResponse(code=200, msg=f"成功更新文件 {kb_file.filename}") - except Exception as e: - logger.error(traceback.format_exc()) - return BaseResponse(code=500, msg=f"{kb_file.filename} 文件更新失败,错误信息是:{e}") - - return BaseResponse(code=500, msg=f"{kb_file.filename} 文件更新失败") - - -async def download_doc( - knowledge_base_name: str = Query(..., examples=["samples"]), - file_name: str = Query(..., examples=["test.txt"]), - kb_root_path: str =Body(""), - ): - ''' - 下载知识库文档 - ''' - if not validate_kb_name(knowledge_base_name): - return BaseResponse(code=403, msg="Don't attack me") - - kb = KBServiceFactory.get_service_by_name(knowledge_base_name, None, kb_root_path) - if kb is None: - return BaseResponse(code=404, msg=f"未找到知识库 {knowledge_base_name}") - - try: - kb_file = DocumentFile(filename=file_name, - knowledge_base_name=knowledge_base_name, - kb_root_path=kb_root_path) - - if os.path.exists(kb_file.filepath): - return FileResponse( - path=kb_file.filepath, - filename=kb_file.filename, - media_type="multipart/form-data") - except Exception as e: - print(e) - return BaseResponse(code=500, msg=f"{kb_file.filename} 读取文件失败,错误信息是:{e}") - - return BaseResponse(code=500, msg=f"{kb_file.filename} 读取文件失败") - - -async def recreate_vector_store( - knowledge_base_name: str = Body(..., examples=["samples"]), - allow_empty_kb: bool = Body(True), - vs_type: str = Body("faiss"), - kb_root_path: str = Body(""), - api_key: bool = Body(..., examples=["samples"]), - api_base_url: bool = Body(..., examples=["samples"]), - embed_model: bool = Body(..., examples=["samples"]), - embed_model_path: bool = Body(..., examples=["samples"]), - model_device: bool = Body(..., examples=["samples"]), - embed_engine: bool = Body(..., examples=["samples"]), - ): - ''' - recreate vector store from the content. - this is usefull when user can copy files to content folder directly instead of upload through network. - by default, get_service_by_name only return knowledge base in the info.db and having document files in it. - set allow_empty_kb to True make it applied on empty knowledge base which it not in the info.db or having no documents. - ''' - embed_config: EmbedConfig = EmbedConfig(**locals()) - async def output(): - kb = KBServiceFactory.get_service(knowledge_base_name, vs_type, embed_config, kb_root_path) - if not kb.exists() and not allow_empty_kb: - yield {"code": 404, "msg": f"未找到知识库 ‘{knowledge_base_name}’"} - else: - kb.create_kb() - kb.clear_vs() - docs = list_docs_from_folder(knowledge_base_name, kb_root_path) - for i, doc in enumerate(docs): - try: - kb_file = DocumentFile(doc, knowledge_base_name, - kb_root_path=kb_root_path) - yield json.dumps({ - "code": 200, - "msg": f"({i + 1} / {len(docs)}): {doc}", - "total": len(docs), - "finished": i, - "doc": doc, - }, ensure_ascii=False) - if i == len(docs) - 1: - not_refresh_vs_cache = False - else: - not_refresh_vs_cache = True - kb.add_doc(kb_file, not_refresh_vs_cache=not_refresh_vs_cache) - except Exception as e: - print(e) - yield json.dumps({ - "code": 500, - "msg": f"添加文件‘{doc}’到知识库‘{knowledge_base_name}’时出错:{e}。已跳过。", - }) - - return StreamingResponse(output(), media_type="text/event-stream") diff --git a/coagent/service/llm_api.py.bak b/coagent/service/llm_api.py.bak deleted file mode 100644 index e69de29..0000000 diff --git a/coagent/service/llm_api.py.bak.bak b/coagent/service/llm_api.py.bak.bak deleted file mode 100644 index b44bb91..0000000 --- a/coagent/service/llm_api.py.bak.bak +++ /dev/null @@ -1,967 +0,0 @@ -############################# Attention ######################## - -# Code copied from -# https://github.com/chatchat-space/Langchain-Chatchat/blob/master/server/llm_api.py - -################################################################# - - - -from multiprocessing import Process, Queue -import multiprocessing as mp -import sys -import os -from typing import List, Union, Dict -import httpx -import asyncio -import datetime -import argparse - -src_dir = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -) -sys.path.append(src_dir) - -sys.path.append(os.path.dirname(os.path.dirname(__file__))) -from configs.model_config import llm_model_dict, LLM_MODEL, LLM_DEVICE, LOG_PATH, logger, LLM_MODELs -from configs.server_config import ( - FSCHAT_CONTROLLER, FSCHAT_MODEL_WORKERS, FSCHAT_OPENAI_API -) -from coagent.service.utils import get_model_worker_config - -from coagent.utils.server_utils import ( - MakeFastAPIOffline, -) -from fastapi import FastAPI - -host_ip = "0.0.0.0" -controller_port = 20001 -model_worker_port = 20002 -openai_api_port = 8888 -base_url = "http://127.0.0.1:{}" - - -os.environ['PATH'] = os.environ.get("PATH", "") + os.pathsep -log_verbose = True - - -def set_httpx_timeout(timeout=60.0): - import httpx - httpx._config.DEFAULT_TIMEOUT_CONFIG.connect = timeout - httpx._config.DEFAULT_TIMEOUT_CONFIG.read = timeout - httpx._config.DEFAULT_TIMEOUT_CONFIG.write = timeout - - -def get_all_model_worker_configs() -> dict: - result = {} - model_names = set(FSCHAT_MODEL_WORKERS.keys()) - for name in model_names: - if name != "default": - result[name] = get_model_worker_config(name) - return result - - -def fschat_controller_address() -> str: - from configs.server_config import FSCHAT_CONTROLLER - - host = FSCHAT_CONTROLLER["host"] - if host == "0.0.0.0": - host = "127.0.0.1" - port = FSCHAT_CONTROLLER["port"] - return f"http://{host}:{port}" - - -def fschat_model_worker_address(model_name: str = LLM_MODEL) -> str: - if model := get_model_worker_config(model_name): # TODO: depends fastchat - host = model["host"] - if host == "0.0.0.0": - host = "127.0.0.1" - port = model["port"] - return f"http://{host}:{port}" - return "" - - -def fschat_openai_api_address() -> str: - from configs.server_config import FSCHAT_OPENAI_API - - host = FSCHAT_OPENAI_API["host"] - if host == "0.0.0.0": - host = "127.0.0.1" - port = FSCHAT_OPENAI_API["port"] - return f"http://{host}:{port}/v1" - - -def set_httpx_config( - timeout: float = 300, - proxy: Union[str, Dict] = None, -): - ''' - 设置httpx默认timeout。httpx默认timeout是5秒,在请求LLM回答时不够用。 - 将本项目相关服务加入无代理列表,避免fastchat的服务器请求错误。(windows下无效) - 对于chatgpt等在线API,如要使用代理需要手动配置。搜索引擎的代理如何处置还需考虑。 - ''' - - import httpx - import os - - httpx._config.DEFAULT_TIMEOUT_CONFIG.connect = timeout - httpx._config.DEFAULT_TIMEOUT_CONFIG.read = timeout - httpx._config.DEFAULT_TIMEOUT_CONFIG.write = timeout - - # 在进程范围内设置系统级代理 - proxies = {} - if isinstance(proxy, str): - for n in ["http", "https", "all"]: - proxies[n + "_proxy"] = proxy - elif isinstance(proxy, dict): - for n in ["http", "https", "all"]: - if p := proxy.get(n): - proxies[n + "_proxy"] = p - elif p := proxy.get(n + "_proxy"): - proxies[n + "_proxy"] = p - - for k, v in proxies.items(): - os.environ[k] = v - - # set host to bypass proxy - no_proxy = [x.strip() for x in os.environ.get("no_proxy", "").split(",") if x.strip()] - no_proxy += [ - # do not use proxy for locahost - "http://127.0.0.1", - "http://localhost", - ] - # do not use proxy for user deployed fastchat servers - for x in [ - fschat_controller_address(), - fschat_model_worker_address(), - fschat_openai_api_address(), - ]: - host = ":".join(x.split(":")[:2]) - if host not in no_proxy: - no_proxy.append(host) - os.environ["NO_PROXY"] = ",".join(no_proxy) - - # TODO: 简单的清除系统代理不是个好的选择,影响太多。似乎修改代理服务器的bypass列表更好。 - # patch requests to use custom proxies instead of system settings - def _get_proxies(): - return proxies - - import urllib.request - urllib.request.getproxies = _get_proxies - - # 自动检查torch可用的设备。分布式部署时,不运行LLM的机器上可以不装torch - - -def get_httpx_client( - use_async: bool = False, - proxies: Union[str, Dict] = None, - timeout: float = 300, - **kwargs, -) -> Union[httpx.Client, httpx.AsyncClient]: - ''' - helper to get httpx client with default proxies that bypass local addesses. - ''' - default_proxies = { - # do not use proxy for locahost - "all://127.0.0.1": None, - "all://localhost": None, - } - # do not use proxy for user deployed fastchat servers - for x in [ - fschat_controller_address(), - fschat_model_worker_address(), - fschat_openai_api_address(), - ]: - host = ":".join(x.split(":")[:2]) - default_proxies.update({host: None}) - - # get proxies from system envionrent - # proxy not str empty string, None, False, 0, [] or {} - default_proxies.update({ - "http://": (os.environ.get("http_proxy") - if os.environ.get("http_proxy") and len(os.environ.get("http_proxy").strip()) - else None), - "https://": (os.environ.get("https_proxy") - if os.environ.get("https_proxy") and len(os.environ.get("https_proxy").strip()) - else None), - "all://": (os.environ.get("all_proxy") - if os.environ.get("all_proxy") and len(os.environ.get("all_proxy").strip()) - else None), - }) - for host in os.environ.get("no_proxy", "").split(","): - if host := host.strip(): - # default_proxies.update({host: None}) # Origin code - default_proxies.update({'all://' + host: None}) # PR 1838 fix, if not add 'all://', httpx will raise error - - # merge default proxies with user provided proxies - if isinstance(proxies, str): - proxies = {"all://": proxies} - - if isinstance(proxies, dict): - default_proxies.update(proxies) - - # construct Client - kwargs.update(timeout=timeout, proxies=default_proxies) - - if log_verbose: - logger.info(f'{get_httpx_client.__class__.__name__}:kwargs: {kwargs}') - - if use_async: - return httpx.AsyncClient(**kwargs) - else: - return httpx.Client(**kwargs) - - -def create_controller_app( - dispatch_method: str, - log_level: str = "INFO", -) -> FastAPI: - import fastchat.constants - fastchat.constants.LOGDIR = LOG_PATH - from fastchat.serve.controller import app, Controller, logger - logger.setLevel(log_level) - - controller = Controller(dispatch_method) - sys.modules["fastchat.serve.controller"].controller = controller - - MakeFastAPIOffline(app) - app.title = "FastChat Controller" - app._controller = controller - return app - - -def create_model_worker_app(log_level: str = "INFO", **kwargs) -> FastAPI: - """ - kwargs包含的字段如下: - host: - port: - model_names:[`model_name`] - controller_address: - worker_address: - - 对于Langchain支持的模型: - langchain_model:True - 不会使用fschat - 对于online_api: - online_api:True - worker_class: `provider` - 对于离线模型: - model_path: `model_name_or_path`,huggingface的repo-id或本地路径 - device:`LLM_DEVICE` - """ - import fastchat.constants - fastchat.constants.LOGDIR = LOG_PATH - import argparse - - parser = argparse.ArgumentParser() - args = parser.parse_args([]) - - for k, v in kwargs.items(): - setattr(args, k, v) - - logger.error(f"可用模型有哪些: {args.model_names}") - - if worker_class := kwargs.get("langchain_model"): #Langchian支持的模型不用做操作 - from fastchat.serve.base_model_worker import app - worker = "" - # 在线模型API - elif worker_class := kwargs.get("worker_class"): - from fastchat.serve.base_model_worker import app - - worker = worker_class(model_names=args.model_names, - controller_addr=args.controller_address, - worker_addr=args.worker_address) - # sys.modules["fastchat.serve.base_model_worker"].worker = worker - sys.modules["fastchat.serve.base_model_worker"].logger.setLevel(log_level) - # 本地模型 - else: - from configs.model_config import VLLM_MODEL_DICT - # if kwargs["model_names"][0] in VLLM_MODEL_DICT and args.infer_turbo == "vllm": - if kwargs["model_names"][0] in VLLM_MODEL_DICT: - import fastchat.serve.vllm_worker - from fastchat.serve.vllm_worker import VLLMWorker, app, worker_id - from vllm import AsyncLLMEngine - from vllm.engine.arg_utils import AsyncEngineArgs,EngineArgs - - args.tokenizer = args.model_path # 如果tokenizer与model_path不一致在此处添加 - args.tokenizer_mode = 'auto' - args.trust_remote_code= True - args.download_dir= None - args.load_format = 'auto' - args.dtype = 'auto' - args.seed = 0 - args.worker_use_ray = False - args.pipeline_parallel_size = 1 - args.tensor_parallel_size = 1 - args.block_size = 16 - args.swap_space = 4 # GiB - args.gpu_memory_utilization = 0.90 - args.max_num_batched_tokens = None # 一个批次中的最大令牌(tokens)数量,这个取决于你的显卡和大模型设置,设置太大显存会不够 - args.max_num_seqs = 256 - args.disable_log_stats = False - args.conv_template = None - args.limit_worker_concurrency = 5 - args.no_register = False - args.num_gpus = 1 # vllm worker的切分是tensor并行,这里填写显卡的数量 - args.engine_use_ray = False - args.disable_log_requests = False - - # 0.2.1 vllm后要加的参数, 但是这里不需要 - args.max_model_len = None - args.revision = None - args.quantization = None - args.max_log_len = None - args.tokenizer_revision = None - - # 0.2.2 vllm需要新加的参数 - args.max_paddings = 256 - - if args.model_path: - args.model = args.model_path - if args.num_gpus > 1: - args.tensor_parallel_size = args.num_gpus - - for k, v in kwargs.items(): - setattr(args, k, v) - - engine_args = AsyncEngineArgs.from_cli_args(args) - engine = AsyncLLMEngine.from_engine_args(engine_args) - - worker = VLLMWorker( - controller_addr = args.controller_address, - worker_addr = args.worker_address, - worker_id = worker_id, - model_path = args.model_path, - model_names = args.model_names, - limit_worker_concurrency = args.limit_worker_concurrency, - no_register = args.no_register, - llm_engine = engine, - conv_template = args.conv_template, - ) - sys.modules["fastchat.serve.vllm_worker"].engine = engine - sys.modules["fastchat.serve.vllm_worker"].worker = worker - sys.modules["fastchat.serve.vllm_worker"].logger.setLevel(log_level) - - else: - from fastchat.serve.model_worker import app, GptqConfig, AWQConfig, ModelWorker, worker_id - - args.gpus = "0" # GPU的编号,如果有多个GPU,可以设置为"0,1,2,3" - args.max_gpu_memory = "22GiB" - args.num_gpus = 1 # model worker的切分是model并行,这里填写显卡的数量 - - args.load_8bit = False - args.cpu_offloading = None - args.gptq_ckpt = None - args.gptq_wbits = 16 - args.gptq_groupsize = -1 - args.gptq_act_order = False - args.awq_ckpt = None - args.awq_wbits = 16 - args.awq_groupsize = -1 - args.model_names = [""] - args.conv_template = None - args.limit_worker_concurrency = 5 - args.stream_interval = 2 - args.no_register = False - args.embed_in_truncate = False - for k, v in kwargs.items(): - setattr(args, k, v) - if args.gpus: - if args.num_gpus is None: - args.num_gpus = len(args.gpus.split(',')) - if len(args.gpus.split(",")) < args.num_gpus: - raise ValueError( - f"Larger --num-gpus ({args.num_gpus}) than --gpus {args.gpus}!" - ) - os.environ["CUDA_VISIBLE_DEVICES"] = args.gpus - gptq_config = GptqConfig( - ckpt=args.gptq_ckpt or args.model_path, - wbits=args.gptq_wbits, - groupsize=args.gptq_groupsize, - act_order=args.gptq_act_order, - ) - awq_config = AWQConfig( - ckpt=args.awq_ckpt or args.model_path, - wbits=args.awq_wbits, - groupsize=args.awq_groupsize, - ) - - worker = ModelWorker( - controller_addr=args.controller_address, - worker_addr=args.worker_address, - worker_id=worker_id, - model_path=args.model_path, - model_names=args.model_names, - limit_worker_concurrency=args.limit_worker_concurrency, - no_register=args.no_register, - device=args.device, - num_gpus=args.num_gpus, - max_gpu_memory=args.max_gpu_memory, - load_8bit=args.load_8bit, - cpu_offloading=args.cpu_offloading, - gptq_config=gptq_config, - awq_config=awq_config, - stream_interval=args.stream_interval, - conv_template=args.conv_template, - embed_in_truncate=args.embed_in_truncate, - ) - sys.modules["fastchat.serve.model_worker"].args = args - sys.modules["fastchat.serve.model_worker"].gptq_config = gptq_config - # sys.modules["fastchat.serve.model_worker"].worker = worker - sys.modules["fastchat.serve.model_worker"].logger.setLevel(log_level) - - MakeFastAPIOffline(app) - app.title = f"FastChat LLM Server ({args.model_names[0]})" - app._worker = worker - return app - - -def create_openai_api_app( - controller_address: str, - api_keys: List = [], - log_level: str = "INFO", -) -> FastAPI: - import fastchat.constants - fastchat.constants.LOGDIR = LOG_PATH - from fastchat.serve.openai_api_server import app, CORSMiddleware, app_settings - from fastchat.utils import build_logger - logger = build_logger("openai_api", "openai_api.log") - logger.setLevel(log_level) - - app.add_middleware( - CORSMiddleware, - allow_credentials=True, - allow_origins=["*"], - allow_methods=["*"], - allow_headers=["*"], - ) - - sys.modules["fastchat.serve.openai_api_server"].logger = logger - app_settings.controller_address = controller_address - app_settings.api_keys = api_keys - - MakeFastAPIOffline(app) - app.title = "FastChat OpeanAI API Server" - return app - - -def _set_app_event(app: FastAPI, started_event: mp.Event = None): - @app.on_event("startup") - async def on_startup(): - if started_event is not None: - started_event.set() - - -def run_controller(log_level: str = "INFO", started_event: mp.Event = None): - import uvicorn - import httpx - from fastapi import Body - import time - import sys - # from server.utils import set_httpx_config - set_httpx_config() - - app = create_controller_app( - dispatch_method=FSCHAT_CONTROLLER.get("dispatch_method"), - log_level=log_level, - ) - _set_app_event(app, started_event) - - # add interface to release and load model worker - @app.post("/release_worker") - def release_worker( - model_name: str = Body(..., description="要释放模型的名称", samples=["chatglm-6b"]), - # worker_address: str = Body(None, description="要释放模型的地址,与名称二选一", samples=[FSCHAT_CONTROLLER_address()]), - new_model_name: str = Body(None, description="释放后加载该模型"), - keep_origin: bool = Body(False, description="不释放原模型,加载新模型") - ) -> Dict: - available_models = app._controller.list_models() - if new_model_name in available_models: - msg = f"要切换的LLM模型 {new_model_name} 已经存在" - logger.info(msg) - return {"code": 500, "msg": msg} - - if new_model_name: - logger.info(f"开始切换LLM模型:从 {model_name} 到 {new_model_name}") - else: - logger.info(f"即将停止LLM模型: {model_name}") - - if model_name not in available_models: - msg = f"the model {model_name} is not available" - logger.error(msg) - return {"code": 500, "msg": msg} - - worker_address = app._controller.get_worker_address(model_name) - if not worker_address: - msg = f"can not find model_worker address for {model_name}" - logger.error(msg) - return {"code": 500, "msg": msg} - - with get_httpx_client() as client: - r = client.post(worker_address + "/release", - json={"new_model_name": new_model_name, "keep_origin": keep_origin}) - if r.status_code != 200: - msg = f"failed to release model: {model_name}" - logger.error(msg) - return {"code": 500, "msg": msg} - - if new_model_name: - timer = 300 # wait for new model_worker register - while timer > 0: - models = app._controller.list_models() - if new_model_name in models: - break - time.sleep(1) - timer -= 1 - if timer > 0: - msg = f"sucess change model from {model_name} to {new_model_name}" - logger.info(msg) - return {"code": 200, "msg": msg} - else: - msg = f"failed change model from {model_name} to {new_model_name}" - logger.error(msg) - return {"code": 500, "msg": msg} - else: - msg = f"sucess to release model: {model_name}" - logger.info(msg) - return {"code": 200, "msg": msg} - - host = FSCHAT_CONTROLLER["host"] - port = FSCHAT_CONTROLLER["port"] - - if log_level == "ERROR": - sys.stdout = sys.__stdout__ - sys.stderr = sys.__stderr__ - - uvicorn.run(app, host=host, port=port, log_level=log_level.lower()) - - -def run_model_worker( - model_name: str = LLM_MODEL, - controller_address: str = "", - log_level: str = "INFO", - q: mp.Queue = None, - started_event: mp.Event = None, -): - import uvicorn - from fastapi import Body - import sys - set_httpx_config() - - kwargs = get_model_worker_config(model_name) - host = kwargs.pop("host") - port = kwargs.pop("port") - kwargs["model_names"] = [model_name] - kwargs["controller_address"] = controller_address or fschat_controller_address() - kwargs["worker_address"] = fschat_model_worker_address(model_name) - model_path = kwargs.get("model_path", "") - kwargs["model_path"] = model_path - # kwargs["gptq_wbits"] = 4 # int4 模型试用这个参数 - - app = create_model_worker_app(log_level=log_level, **kwargs) - _set_app_event(app, started_event) - if log_level == "ERROR": - sys.stdout = sys.__stdout__ - sys.stderr = sys.__stderr__ - - # add interface to release and load model - @app.post("/release") - def release_model( - new_model_name: str = Body(None, description="释放后加载该模型"), - keep_origin: bool = Body(False, description="不释放原模型,加载新模型") - ) -> Dict: - if keep_origin: - if new_model_name: - q.put([model_name, "start", new_model_name]) - else: - if new_model_name: - q.put([model_name, "replace", new_model_name]) - else: - q.put([model_name, "stop", None]) - return {"code": 200, "msg": "done"} - - uvicorn.run(app, host=host, port=port, log_level=log_level.lower()) - - -def run_openai_api(log_level: str = "INFO", started_event: mp.Event = None): - import uvicorn - import sys - set_httpx_config() - - controller_addr = fschat_controller_address() - app = create_openai_api_app(controller_addr, log_level=log_level) # TODO: not support keys yet. - _set_app_event(app, started_event) - - host = FSCHAT_OPENAI_API["host"] - port = FSCHAT_OPENAI_API["port"] - if log_level == "ERROR": - sys.stdout = sys.__stdout__ - sys.stderr = sys.__stderr__ - uvicorn.run(app, host=host, port=port) - - -def parse_args() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser() - parser.add_argument( - "-a", - "--all-webui", - action="store_true", - help="run fastchat's controller/openai_api/model_worker servers, run api.py and webui.py", - dest="all_webui", - ) - parser.add_argument( - "--all-api", - action="store_true", - help="run fastchat's controller/openai_api/model_worker servers, run api.py", - dest="all_api", - ) - parser.add_argument( - "--llm-api", - action="store_true", - help="run fastchat's controller/openai_api/model_worker servers", - dest="llm_api", - ) - parser.add_argument( - "-o", - "--openai-api", - action="store_true", - help="run fastchat's controller/openai_api servers", - dest="openai_api", - ) - parser.add_argument( - "-m", - "--model-worker", - action="store_true", - help="run fastchat's model_worker server with specified model name. " - "specify --model-name if not using default LLM_MODELS", - dest="model_worker", - ) - parser.add_argument( - "-n", - "--model-name", - type=str, - nargs="+", - default=LLM_MODELs, - help="specify model name for model worker. " - "add addition names with space seperated to start multiple model workers.", - dest="model_name", - ) - parser.add_argument( - "-c", - "--controller", - type=str, - help="specify controller address the worker is registered to. default is FSCHAT_CONTROLLER", - dest="controller_address", - ) - parser.add_argument( - "--api", - action="store_true", - help="run api.py server", - dest="api", - ) - parser.add_argument( - "-p", - "--api-worker", - action="store_true", - help="run online model api such as zhipuai", - dest="api_worker", - ) - parser.add_argument( - "-w", - "--webui", - action="store_true", - help="run webui.py server", - dest="webui", - ) - parser.add_argument( - "-q", - "--quiet", - action="store_true", - help="减少fastchat服务log信息", - dest="quiet", - ) - parser.add_argument( - "-i", - "--lite", - action="store_true", - help="以Lite模式运行:仅支持在线API的LLM对话、搜索引擎对话", - dest="lite", - ) - args = parser.parse_args() - return args, parser - - -def dump_server_info(after_start=False, args=None): - import platform - import langchain - import fastchat - - print("\n") - print("=" * 30 + "Langchain-Chatchat Configuration" + "=" * 30) - print(f"操作系统:{platform.platform()}.") - print(f"python版本:{sys.version}") - print(f"langchain版本:{langchain.__version__}. fastchat版本:{fastchat.__version__}") - print("\n") - - models = LLM_MODELs - if args and args.model_name: - models = args.model_name - - print(f"当前启动的LLM模型:{models} @ {LLM_DEVICE}") - - for model in models: - print(get_model_worker_config(model)) - - if after_start: - print("\n") - print(f"服务端运行信息:") - print(f" OpenAI API Server: {fschat_openai_api_address()}") - print("\n") - - -async def start_main_server(): - import time - import signal - - def handler(signalname): - """ - Python 3.9 has `signal.strsignal(signalnum)` so this closure would not be needed. - Also, 3.8 includes `signal.valid_signals()` that can be used to create a mapping for the same purpose. - """ - def f(signal_received, frame): - raise KeyboardInterrupt(f"{signalname} received") - return f - - # This will be inherited by the child process if it is forked (not spawned) - signal.signal(signal.SIGINT, handler("SIGINT")) - signal.signal(signal.SIGTERM, handler("SIGTERM")) - - mp.set_start_method("spawn") - manager = mp.Manager() - run_mode = None - - queue = manager.Queue() - args, parser = parse_args() - logger.debug(f"args: {args}") - - dump_server_info(args=args) - - if len(sys.argv) > 1: - logger.info(f"正在启动服务:") - logger.info(f"如需查看 llm_api 日志,请前往 {LOG_PATH}") - - processes = {"online_api": {}, "model_worker": {}} - - def process_count(): - return len(processes) + len(processes["online_api"]) + len(processes["model_worker"]) - 2 - - if args.quiet or not log_verbose: - log_level = "ERROR" - else: - log_level = "INFO" - - controller_started = manager.Event() - process = Process( - target=run_controller, - name=f"controller", - kwargs=dict(log_level=log_level, started_event=controller_started), - daemon=True, - ) - processes["controller"] = process - - process = Process( - target=run_openai_api, - name=f"openai_api", - daemon=True, - ) - processes["openai_api"] = process - - model_worker_started = [] - for model_name in args.model_name: - config = get_model_worker_config(model_name) - if not config.get("online_api"): - e = manager.Event() - model_worker_started.append(e) - process = Process( - target=run_model_worker, - name=f"model_worker - {model_name}", - kwargs=dict(model_name=model_name, - controller_address=args.controller_address, - log_level=log_level, - q=queue, - started_event=e), - daemon=True, - ) - processes["model_worker"][model_name] = process - - - for model_name in args.model_name: - config = get_model_worker_config(model_name) - logger.error(f"config: {config}, {model_name}, {FSCHAT_MODEL_WORKERS.keys()}") - if (config.get("online_api") - and config.get("worker_class") - and model_name in FSCHAT_MODEL_WORKERS): - e = manager.Event() - model_worker_started.append(e) - process = Process( - target=run_model_worker, - name=f"api_worker - {model_name}", - kwargs=dict(model_name=model_name, - controller_address=args.controller_address, - log_level=log_level, - q=queue, - started_event=e), - daemon=True, - ) - processes["online_api"][model_name] = process - - - if process_count() == 0: - parser.print_help() - else: - try: - # 保证任务收到SIGINT后,能够正常退出 - if p:= processes.get("controller"): - p.start() - p.name = f"{p.name} ({p.pid})" - controller_started.wait() # 等待controller启动完成 - - if p:= processes.get("openai_api"): - p.start() - p.name = f"{p.name} ({p.pid})" - - for n, p in processes.get("model_worker", {}).items(): - p.start() - p.name = f"{p.name} ({p.pid})" - - for n, p in processes.get("online_api", []).items(): - p.start() - p.name = f"{p.name} ({p.pid})" - - # 等待所有model_worker启动完成 - for e in model_worker_started: - e.wait() - - dump_server_info(after_start=True, args=args) - - while True: - cmd = queue.get() # 收到切换模型的消息 - e = manager.Event() - if isinstance(cmd, list): - model_name, cmd, new_model_name = cmd - if cmd == "start": # 运行新模型 - logger.info(f"准备启动新模型进程:{new_model_name}") - process = Process( - target=run_model_worker, - name=f"model_worker - {new_model_name}", - kwargs=dict(model_name=new_model_name, - controller_address=args.controller_address, - log_level=log_level, - q=queue, - started_event=e), - daemon=True, - ) - process.start() - process.name = f"{process.name} ({process.pid})" - processes["model_worker"][new_model_name] = process - e.wait() - logger.info(f"成功启动新模型进程:{new_model_name}") - elif cmd == "stop": - if process := processes["model_worker"].get(model_name): - time.sleep(1) - process.terminate() - process.join() - logger.info(f"停止模型进程:{model_name}") - else: - logger.error(f"未找到模型进程:{model_name}") - elif cmd == "replace": - if process := processes["model_worker"].pop(model_name, None): - logger.info(f"停止模型进程:{model_name}") - start_time = datetime.now() - time.sleep(1) - process.terminate() - process.join() - process = Process( - target=run_model_worker, - name=f"model_worker - {new_model_name}", - kwargs=dict(model_name=new_model_name, - controller_address=args.controller_address, - log_level=log_level, - q=queue, - started_event=e), - daemon=True, - ) - process.start() - process.name = f"{process.name} ({process.pid})" - processes["model_worker"][new_model_name] = process - e.wait() - timing = datetime.now() - start_time - logger.info(f"成功启动新模型进程:{new_model_name}。用时:{timing}。") - else: - logger.error(f"未找到模型进程:{model_name}") - - - # for process in processes.get("model_worker", {}).values(): - # process.join() - # for process in processes.get("online_api", {}).values(): - # process.join() - - # for name, process in processes.items(): - # if name not in ["model_worker", "online_api"]: - # if isinstance(p, dict): - # for work_process in p.values(): - # work_process.join() - # else: - # process.join() - except Exception as e: - logger.error(e) - logger.warning("Caught KeyboardInterrupt! Setting stop event...") - finally: - # Send SIGINT if process doesn't exit quickly enough, and kill it as last resort - # .is_alive() also implicitly joins the process (good practice in linux) - # while alive_procs := [p for p in processes.values() if p.is_alive()]: - - for p in processes.values(): - logger.warning("Sending SIGKILL to %s", p) - # Queues and other inter-process communication primitives can break when - # process is killed, but we don't care here - - if isinstance(p, dict): - for process in p.values(): - process.kill() - else: - p.kill() - - for p in processes.values(): - logger.info("Process status: %s", p) - - -if __name__ == "__main__": - - if sys.version_info < (3, 10): - loop = asyncio.get_event_loop() - else: - try: - loop = asyncio.get_running_loop() - except RuntimeError: - loop = asyncio.new_event_loop() - - asyncio.set_event_loop(loop) - # 同步调用协程代码 - loop.run_until_complete(start_main_server()) - - -# 服务启动后接口调用示例: -# import openai -# openai.api_key = "EMPTY" # Not support yet -# openai.api_base = "http://localhost:8888/v1" - -# model = "chatglm2-6b" - -# # create a chat completion -# completion = openai.ChatCompletion.create( -# model=model, -# messages=[{"role": "user", "content": "Hello! What is your name?"}] -# ) -# # print the completion -# print(completion.choices[0].message.content) diff --git a/coagent/service/migrate.py b/coagent/service/migrate.py deleted file mode 100644 index f5643b7..0000000 --- a/coagent/service/migrate.py +++ /dev/null @@ -1,141 +0,0 @@ -import os -from typing import Literal, Callable, Any - -# from configs.model_config import EMBEDDING_MODEL, DEFAULT_VS_TYPE, KB_ROOT_PATH - -from coagent.orm.utils import DocumentFile -from coagent.orm.commands import add_doc_to_db -from coagent.utils.path_utils import * -from .service_factory import KBServiceFactory - - -''' -这个方法不存在作用,可无视 -''' -def folder2db( - kb_name: str, - mode: Literal["recreate_vs", "fill_info_only", "update_in_db", "increament"], - vs_type: Literal["faiss", "milvus", "pg", "chromadb"] = "faiss", - embed_model: str = "text2vec-base-chinese", - kb_root_path: str = "", - callback_before: Callable = None, - callback_after: Callable = None, -): - ''' - use existed files in local folder to populate database and/or vector store. - set parameter `mode` to: - recreate_vs: recreate all vector store and fill info to database using existed files in local folder - fill_info_only: do not create vector store, fill info to db using existed files only - update_in_db: update vector store and database info using local files that existed in database only - increament: create vector store and database info for local files that not existed in database only - ''' - kb = KBServiceFactory.get_service(kb_name, vs_type, embed_model) - kb.create_kb() - - if mode == "recreate_vs": - kb.clear_vs() - docs = list_docs_from_folder(kb_name, kb_root_path) - for i, doc in enumerate(docs): - try: - kb_file = DocumentFile(doc, kb_name, kb_root_path) - if callable(callback_before): - callback_before(kb_file, i, docs) - if i == len(docs) - 1: - not_refresh_vs_cache = False - else: - not_refresh_vs_cache = True - kb.add_doc(kb_file, not_refresh_vs_cache=not_refresh_vs_cache) - if callable(callback_after): - callback_after(kb_file, i, docs) - except Exception as e: - print(e) - elif mode == "fill_info_only": - docs = list_docs_from_folder(kb_name, kb_root_path) - for i, doc in enumerate(docs): - try: - kb_file = DocumentFile(doc, kb_name, kb_root_path) - if callable(callback_before): - callback_before(kb_file, i, docs) - add_doc_to_db(kb_file) - if callable(callback_after): - callback_after(kb_file, i, docs) - except Exception as e: - print(e) - elif mode == "update_in_db": - docs = kb.list_docs() - for i, doc in enumerate(docs): - try: - kb_file = DocumentFile(doc, kb_name, kb_root_path) - if callable(callback_before): - callback_before(kb_file, i, docs) - if i == len(docs) - 1: - not_refresh_vs_cache = False - else: - not_refresh_vs_cache = True - kb.update_doc(kb_file, not_refresh_vs_cache=not_refresh_vs_cache) - if callable(callback_after): - callback_after(kb_file, i, docs) - except Exception as e: - print(e) - elif mode == "increament": - db_docs = kb.list_docs() - folder_docs = list_docs_from_folder(kb_name, kb_root_path) - docs = list(set(folder_docs) - set(db_docs)) - for i, doc in enumerate(docs): - try: - kb_file = DocumentFile(doc, kb_name, kb_root_path) - if callable(callback_before): - callback_before(kb_file, i, docs) - if i == len(docs) - 1: - not_refresh_vs_cache = False - else: - not_refresh_vs_cache = True - kb.add_doc(kb_file, not_refresh_vs_cache=not_refresh_vs_cache) - if callable(callback_after): - callback_after(kb_file, i, docs) - except Exception as e: - print(e) - else: - raise ValueError(f"unspported migrate mode: {mode}") - - -def recreate_all_vs( - vs_type: Literal["faiss", "milvus", "pg", "chromadb"] = "faiss", - embed_mode: str = "text2vec-base-chinese", - kb_root_path: str = "", - **kwargs: Any, -): - ''' - used to recreate a vector store or change current vector store to another type or embed_model - ''' - for kb_name in list_kbs_from_folder(kb_root_path): - folder2db(kb_name, "recreate_vs", vs_type, embed_mode, kb_root_path, **kwargs) - - -def prune_db_docs(kb_name: str, kb_root_path: str = "",): - ''' - delete docs in database that not existed in local folder. - it is used to delete database docs after user deleted some doc files in file browser - ''' - kb = KBServiceFactory.get_service_by_name(kb_name, kb_root_path) - if kb.exists(): - docs_in_db = kb.list_docs() - docs_in_folder = list_docs_from_folder(kb_name, kb_root_path) - docs = list(set(docs_in_db) - set(docs_in_folder)) - for doc in docs: - kb.delete_doc(DocumentFile(doc, kb_name, kb_root_path)) - return docs - -def prune_folder_docs(kb_name: str, kb_root_path=""): - ''' - delete doc files in local folder that not existed in database. - is is used to free local disk space by delete unused doc files. - ''' - kb = KBServiceFactory.get_service_by_name(kb_name, kb_root_path) - if kb.exists(): - docs_in_db = kb.list_docs() - docs_in_folder = list_docs_from_folder(kb_name, kb_root_path) - docs = list(set(docs_in_folder) - set(docs_in_db)) - for doc in docs: - os.remove(get_file_path(kb_name, doc, kb_root_path)) - return docs diff --git a/coagent/service/sdfile_api.py.bak b/coagent/service/sdfile_api.py.bak deleted file mode 100644 index 8f601f4..0000000 --- a/coagent/service/sdfile_api.py.bak +++ /dev/null @@ -1,132 +0,0 @@ -import sys, os, json, traceback, uvicorn, argparse - -src_dir = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -) -sys.path.append(src_dir) - -from loguru import logger - -from fastapi import FastAPI -from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import StreamingResponse, FileResponse -from fastapi import File, UploadFile - -from coagent.utils.server_utils import BaseResponse, ListResponse, DataResponse -# from configs.server_config import OPEN_CROSS_DOMAIN, SDFILE_API_SERVER -from configs.model_config import ( - JUPYTER_WORK_PATH -) - -VERSION = "v0.1.0" - -async def sd_upload_file(file: UploadFile = File(...), work_dir: str = JUPYTER_WORK_PATH): - # 保存上传的文件到服务器 - try: - content = await file.read() - with open(os.path.join(work_dir, file.filename), "wb") as f: - f.write(content) - return {"data": True} - except: - return {"data": False} - - -async def sd_download_file(filename: str, save_filename: str = "filename_to_download.ext", work_dir: str = JUPYTER_WORK_PATH): - # 从服务器下载文件 - logger.debug(f"{os.path.join(work_dir, filename)}") - return {"data": os.path.join(work_dir, filename), "filename": save_filename} - # return {"data": FileResponse(os.path.join(work_dir, filename), filename=save_filename)} - - -async def sd_list_files(work_dir: str = JUPYTER_WORK_PATH): - # 去除目录 - return {"data": os.listdir(work_dir)} - - -async def sd_delete_file(filename: str, work_dir: str = JUPYTER_WORK_PATH): - # 去除目录 - try: - os.remove(os.path.join(work_dir, filename)) - return {"data": True} - except: - return {"data": False} - - -def create_app(open_cross_domain, version=VERSION): - app = FastAPI( - title="DevOps-ChatBot API Server", - version=version - ) - # MakeFastAPIOffline(app) - # Add CORS middleware to allow all origins - # 在config.py中设置OPEN_DOMAIN=True,允许跨域 - # set OPEN_DOMAIN=True in config.py to allow cross-domain - if open_cross_domain: - # if OPEN_CROSS_DOMAIN: - app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], - ) - - app.post("/sdfiles/upload", - tags=["files upload and download"], - response_model=BaseResponse, - summary="上传文件到沙盒" - )(sd_upload_file) - - app.get("/sdfiles/download", - tags=["files upload and download"], - response_model=DataResponse, - summary="从沙盒下载文件" - )(sd_download_file) - - app.get("/sdfiles/list", - tags=["files upload and download"], - response_model=ListResponse, - summary="从沙盒工作目录展示文件" - )(sd_list_files) - - app.get("/sdfiles/delete", - tags=["files upload and download"], - response_model=BaseResponse, - summary="从沙盒工作目录中删除文件" - )(sd_delete_file) - return app - - - -def run_api(host, port, open_cross_domain, **kwargs): - app = create_app(open_cross_domain) - if kwargs.get("ssl_keyfile") and kwargs.get("ssl_certfile"): - uvicorn.run(app, - host=host, - port=port, - ssl_keyfile=kwargs.get("ssl_keyfile"), - ssl_certfile=kwargs.get("ssl_certfile"), - ) - else: - uvicorn.run(app, host=host, port=port) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(prog='DevOps-ChatBot', - description='About DevOps-ChatBot, local knowledge based LLM with langchain' - ' | 基于本地知识库的 LLM 问答') - parser.add_argument("--host", type=str, default="0.0.0.0") - parser.add_argument("--port", type=int, default="7862") - # parser.add_argument("--port", type=int, default=SDFILE_API_SERVER["port"]) - parser.add_argument("--open_cross_domain", type=bool, default=False) - parser.add_argument("--ssl_keyfile", type=str) - parser.add_argument("--ssl_certfile", type=str) - # 初始化消息 - args = parser.parse_args() - args_dict = vars(args) - run_api(host=args.host, - port=args.port, - open_cross_domain=args.open_cross_domain, - ssl_keyfile=args.ssl_keyfile, - ssl_certfile=args.ssl_certfile, - ) \ No newline at end of file diff --git a/coagent/service/service_factory.py b/coagent/service/service_factory.py deleted file mode 100644 index c5ab2d5..0000000 --- a/coagent/service/service_factory.py +++ /dev/null @@ -1,148 +0,0 @@ -from typing import List, Union, Dict -import os - -# from configs.model_config import EMBEDDING_MODEL, KB_ROOT_PATH -from coagent.base_configs.env_config import KB_ROOT_PATH - -from .faiss_db_service import FaissKBService -from .base_service import KBService, SupportedVSType -from coagent.orm.commands import * -from coagent.utils.path_utils import * -from coagent.llm_models.llm_config import EmbedConfig - - -class KBServiceFactory: - - @staticmethod - def get_service(kb_name: str, - vector_store_type: Union[str, SupportedVSType], - # embed_model: str = "text2vec-base-chinese", - embed_config: EmbedConfig, - kb_root_path: str = KB_ROOT_PATH - ) -> KBService: - if isinstance(vector_store_type, str): - vector_store_type = getattr(SupportedVSType, vector_store_type.upper()) - if SupportedVSType.FAISS == vector_store_type: - return FaissKBService(kb_name, embed_config=embed_config, kb_root_path=kb_root_path) - # if SupportedVSType.PG == vector_store_type: - # from server.knowledge_base.kb_service.pg_kb_service import PGKBService - # return PGKBService(kb_name, embed_model=embed_model) - # elif SupportedVSType.MILVUS == vector_store_type: - # from server.knowledge_base.kb_service.milvus_kb_service import MilvusKBService - # return MilvusKBService(kb_name, embed_model=embed_model) # other milvus parameters are set in model_config.kbs_config - # elif SupportedVSType.DEFAULT == vector_store_type: # kb_exists of default kbservice is False, to make validation easier. - # from server.knowledge_base.kb_service.default_kb_service import DefaultKBService - # return DefaultKBService(kb_name) - - @staticmethod - def get_service_by_name(kb_name: str, - embed_config: EmbedConfig, - kb_root_path: str = KB_ROOT_PATH - ) -> KBService: - _, vs_type, embed_model = load_kb_from_db(kb_name) - if vs_type is None and os.path.isdir(get_kb_path(kb_name, kb_root_path)): # faiss knowledge base not in db - vs_type = "faiss" - return KBServiceFactory.get_service(kb_name, vs_type, embed_config, kb_root_path) - - @staticmethod - def get_default(): - return KBServiceFactory.get_service("default", SupportedVSType.DEFAULT, EmbedConfig(), kb_root_path=KB_ROOT_PATH) - - -def get_kb_details(kb_root_path: str) -> List[Dict]: - kbs_in_folder = list_kbs_from_folder(kb_root_path) - kbs_in_db = KBService.list_kbs() - result = {} - - for kb in kbs_in_folder: - result[kb] = { - "kb_name": kb, - "vs_type": "", - "embed_model": "", - "file_count": 0, - "create_time": None, - "in_folder": True, - "in_db": False, - } - - for kb in kbs_in_db: - kb_detail = get_kb_detail(kb) - if kb_detail: - kb_detail["in_db"] = True - if kb in result: - result[kb].update(kb_detail) - else: - kb_detail["in_folder"] = False - result[kb] = kb_detail - - data = [] - # filter not in db' knowledge base docs - result = {k: v for k, v in result.items() if v.get("in_db", False)} - for i, v in enumerate(result.values()): - v['No'] = i + 1 - data.append(v) - - return data - -def get_cb_details() -> List[Dict]: - ''' - get codebase details - @return: list of data - ''' - res = {} - cbs_in_db = list_cbs_from_db() - for cb in cbs_in_db: - cb_detail = get_cb_detail(cb) - res[cb] = cb_detail - - data = [] - for i, v in enumerate(res.values()): - v['No'] = i + 1 - data.append(v) - return data - -def get_cb_details_by_cb_name(cb_name) -> dict: - ''' - get codebase details by cb_name - @return: list of data - ''' - cb_detail = get_cb_detail(cb_name) - return cb_detail - - - - -def get_kb_doc_details(kb_name: str, kb_root_path) -> List[Dict]: - kb = KBServiceFactory.get_service_by_name(kb_name, kb_root_path) - docs_in_folder = list_docs_from_folder(kb_name, kb_root_path) - docs_in_db = kb.list_docs() - result = {} - - for doc in docs_in_folder: - result[doc] = { - "kb_name": kb_name, - "file_name": doc, - "file_ext": os.path.splitext(doc)[-1], - "file_version": 0, - "document_loader": "", - "text_splitter": "", - "create_time": None, - "in_folder": True, - "in_db": False, - } - for doc in docs_in_db: - doc_detail = get_file_detail(kb_name, doc) - if doc_detail: - doc_detail["in_db"] = True - if doc in result: - result[doc].update(doc_detail) - else: - doc_detail["in_folder"] = False - result[doc] = doc_detail - - data = [] - for i, v in enumerate(result.values()): - v['No'] = i + 1 - data.append(v) - - return data diff --git a/coagent/text_splitter/__init__.py b/coagent/text_splitter/__init__.py deleted file mode 100644 index 8867d74..0000000 --- a/coagent/text_splitter/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .langchain_splitter import LCTextSplitter - -__all__ = ["LCTextSplitter"] \ No newline at end of file diff --git a/coagent/text_splitter/langchain_splitter.py b/coagent/text_splitter/langchain_splitter.py deleted file mode 100644 index 4b53025..0000000 --- a/coagent/text_splitter/langchain_splitter.py +++ /dev/null @@ -1,77 +0,0 @@ -import os -import importlib -from loguru import logger - -from langchain.document_loaders.base import BaseLoader -from langchain.text_splitter import ( - SpacyTextSplitter, RecursiveCharacterTextSplitter -) - -# from configs.model_config import ( -# CHUNK_SIZE, -# OVERLAP_SIZE, -# ZH_TITLE_ENHANCE -# ) -from coagent.utils.path_utils import * - - - -class LCTextSplitter: - '''langchain textsplitter 执行file2text''' - def __init__( - self, filepath: str, text_splitter_name: str = None, - chunk_size: int = 500, - overlap_size: int = 50 - ): - self.filepath = filepath - self.ext = os.path.splitext(filepath)[-1].lower() - self.text_splitter_name = text_splitter_name - self.chunk_size = chunk_size - self.overlap_size = overlap_size - if self.ext not in SUPPORTED_EXTS: - raise ValueError(f"暂未支持的文件格式 {self.ext}") - self.document_loader_name = get_LoaderClass(self.ext) - - def file2text(self, ): - loader = self._load_document() - text_splitter = self._load_text_splitter() - if self.document_loader_name in ["JSONLoader", "JSONLLoader"]: - # docs = loader.load() - docs = loader.load_and_split(text_splitter) - # logger.debug(f"please check your file can be loaded, docs.lens {len(docs)}") - else: - docs = loader.load_and_split(text_splitter) - - return docs - - def _load_document(self, ) -> BaseLoader: - DocumentLoader = EXT2LOADER_DICT[self.ext] - if self.document_loader_name == "UnstructuredFileLoader": - loader = DocumentLoader(self.filepath, autodetect_encoding=True) - else: - loader = DocumentLoader(self.filepath) - return loader - - def _load_text_splitter(self, ): - try: - if self.text_splitter_name is None: - text_splitter = SpacyTextSplitter( - pipeline="zh_core_web_sm", - chunk_size=self.chunk_size, - chunk_overlap=self.overlap_size, - ) - self.text_splitter_name = "SpacyTextSplitter" - # elif self.document_loader_name in ["JSONLoader", "JSONLLoader"]: - # text_splitter = None - else: - text_splitter_module = importlib.import_module('langchain.text_splitter') - TextSplitter = getattr(text_splitter_module, self.text_splitter_name) - text_splitter = TextSplitter( - chunk_size=self.chunk_size, - chunk_overlap=self.overlap_size) - except Exception as e: - text_splitter = RecursiveCharacterTextSplitter( - chunk_size=self.chunk_size, - chunk_overlap=self.overlap_size, - ) - return text_splitter diff --git a/coagent/text_splitter/utils.py b/coagent/text_splitter/utils.py deleted file mode 100644 index e69de29..0000000 diff --git a/coagent/tools/__init__.py b/coagent/tools/__init__.py deleted file mode 100644 index 5200c04..0000000 --- a/coagent/tools/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -import importlib - -from .base_tool import toLangchainTools, get_tool_schema, BaseToolModel -from .weather import WeatherInfo, DistrictInfo -from .multiplier import Multiplier -from .world_time import WorldTimeGetTimezoneByArea -from .abnormal_detection import KSigmaDetector -from .metrics_query import MetricsQuery -from .duckduckgo_search import DDGSTool -from .docs_retrieval import DocRetrieval -from .cb_query_tool import CodeRetrieval -from .ocr_tool import BaiduOcrTool -from .stock_tool import StockInfo, StockName -from .codechat_tools import CodeRetrievalSingle, RelatedVerticesRetrival, Vertex2Code - - -IMPORT_TOOL = [ - WeatherInfo, DistrictInfo, Multiplier, WorldTimeGetTimezoneByArea, - KSigmaDetector, MetricsQuery, DDGSTool, DocRetrieval, CodeRetrieval, - BaiduOcrTool, StockInfo, StockName, CodeRetrievalSingle, RelatedVerticesRetrival, Vertex2Code -] - -TOOL_SETS = [tool.__name__ for tool in IMPORT_TOOL] - -TOOL_DICT = {tool.__name__: tool for tool in IMPORT_TOOL} - - -__all__ = [ - "toLangchainTools", "get_tool_schema", "tool_sets", "BaseToolModel" -] + TOOL_SETS - diff --git a/coagent/tools/abnormal_detection.py b/coagent/tools/abnormal_detection.py deleted file mode 100644 index 3b222f7..0000000 --- a/coagent/tools/abnormal_detection.py +++ /dev/null @@ -1,45 +0,0 @@ - -import json -import os -import re -from pydantic import BaseModel, Field -from typing import List, Dict -import requests -import numpy as np -from loguru import logger - -from .base_tool import BaseToolModel - - - -class KSigmaDetector(BaseToolModel): - """ - Tips: - default control Required, e.g. key1 is not Required/key2 is Required - """ - - name: str = "KSigmaDetector" - description: str = "Anomaly detection using K-Sigma method" - - class ToolInputArgs(BaseModel): - """Input for KSigmaDetector.""" - - data: List[float] = Field(..., description="List of data points") - detect_window: int = Field(default=5, description="The size of the detect window for detecting anomalies") - abnormal_window: int = Field(default=3, description="The threshold for the number of abnormal points required to classify the data as abnormal") - k: float = Field(default=3.0, description="the coef of k-sigma") - - class ToolOutputArgs(BaseModel): - """Output for KSigmaDetector.""" - - is_abnormal: bool = Field(..., description="Indicates whether the input data is abnormal or not") - - @staticmethod - def run(data, detect_window=5, abnormal_window=3, k=3.0): - refer_data = np.array(data[-detect_window:]) - detect_data = np.array(data[:-detect_window]) - mean = np.mean(refer_data) - std = np.std(refer_data) - - is_abnormal = np.sum(np.abs(detect_data - mean) > k * std) >= abnormal_window - return {"is_abnormal": is_abnormal} \ No newline at end of file diff --git a/coagent/tools/base_tool.py b/coagent/tools/base_tool.py deleted file mode 100644 index 507822a..0000000 --- a/coagent/tools/base_tool.py +++ /dev/null @@ -1,79 +0,0 @@ -from langchain.agents import Tool -from langchain.tools import StructuredTool -from langchain.tools.base import ToolException -from pydantic import BaseModel, Field -from typing import List, Dict -# import jsonref -import json - - -class BaseToolModel: - name = "BaseToolModel" - description = "Tool Description" - - class ToolInputArgs(BaseModel): - """ - Input for MoveFileTool. - Tips: - default control Required, e.g. key1 is not Required/key2 is Required - """ - - key1: str = Field(default=None, description="hello world!") - key2: str = Field(..., description="hello world!!") - - class ToolOutputArgs(BaseModel): - """ - Input for MoveFileTool. - Tips: - default control Required, e.g. key1 is not Required/key2 is Required - """ - - key1: str = Field(default=None, description="hello world!") - key2: str = Field(..., description="hello world!!") - - @classmethod - def run(cls, tool_input_args: ToolInputArgs) -> ToolOutputArgs: - """excute your tool!""" - pass - - -class BaseTools: - tools: List[BaseToolModel] - - -def get_tool_schema(tool: BaseToolModel) -> Dict: - '''转json schema结构''' - data = jsonref.loads(tool.schema_json()) - _ = json.dumps(data, indent=4) - del data["definitions"] - return data - - -def _handle_error(error: ToolException) -> str: - return ( - "The following errors occurred during tool execution:" - + error.args[0] - + "Please try again." - ) - -import requests -from loguru import logger -def fff(city, extensions): - url = "https://restapi.amap.com/v3/weather/weatherInfo" - json_data = {"key": "4ceb2ef6257a627b72e3be6beab5b059", "city": city, "extensions": extensions} - logger.debug(f"json_data: {json_data}") - res = requests.get(url, params={"key": "4ceb2ef6257a627b72e3be6beab5b059", "city": city, "extensions": extensions}) - return res.json() - - -def toLangchainTools(tools: BaseTools) -> List: - '''''' - return [ - StructuredTool( - name=tool.name, - func=tool.run, - description=tool.description, - args_schema=tool.ToolInputArgs, - handle_tool_error=_handle_error, - ) for tool in tools - ] diff --git a/coagent/tools/cb_query_tool.py b/coagent/tools/cb_query_tool.py deleted file mode 100644 index 3d11b4e..0000000 --- a/coagent/tools/cb_query_tool.py +++ /dev/null @@ -1,69 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: cb_query_tool.py -@time: 2023/11/2 下午4:41 -@desc: -''' -from pydantic import BaseModel, Field -from loguru import logger - -from coagent.llm_models import LLMConfig, EmbedConfig -from .base_tool import BaseToolModel -from coagent.service.cb_api import search_code - - -class CodeRetrieval(BaseToolModel): - name = "CodeRetrieval" - description = "采用知识图谱从本地代码知识库获取相关代码" - - class ToolInputArgs(BaseModel): - query: str = Field(..., description="检索的关键字或问题") - code_base_name: str = Field(..., description="知识库名称", examples=["samples"]) - code_limit: int = Field(1, description="检索返回的数量") - - class ToolOutputArgs(BaseModel): - """Output for MetricsQuery.""" - code: str = Field(..., description="检索代码") - - @classmethod - def run(cls, - code_base_name, - query, - code_limit=1, - history_node_list=[], - search_type="tag", - llm_config: LLMConfig=None, - embed_config: EmbedConfig=None, - use_nh: str=True, - local_graph_path: str='' - ): - """excute your tool!""" - - search_type = { - '基于 cypher': 'cypher', - '基于标签': 'tag', - '基于描述': 'description', - 'tag': 'tag', - 'description': 'description', - 'cypher': 'cypher' - }.get(search_type, 'tag') - - # default - codes = search_code(code_base_name, query, code_limit, search_type=search_type, history_node_list=history_node_list, - embed_engine=embed_config.embed_engine, embed_model=embed_config.embed_model, embed_model_path=embed_config.embed_model_path, - model_device=embed_config.model_device, model_name=llm_config.model_name, temperature=llm_config.temperature, - api_base_url=llm_config.api_base_url, api_key=llm_config.api_key, use_nh=use_nh, - local_graph_path=local_graph_path, embed_config=embed_config - ) - return_codes = [] - context = codes['context'] - related_nodes = codes['related_vertices'] - logger.debug(f"{code_base_name}, {query}, {code_limit}, {search_type}") - logger.debug(f"context: {context}, related_nodes: {related_nodes}") - - return_codes.append({'index': 0, 'code': context, "related_nodes": related_nodes}) - - return return_codes - - diff --git a/coagent/tools/codechat_tools.py b/coagent/tools/codechat_tools.py deleted file mode 100644 index b99c4dc..0000000 --- a/coagent/tools/codechat_tools.py +++ /dev/null @@ -1,116 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: codechat_tools.py.py -@time: 2023/12/14 上午10:24 -@desc: -''' -import os -from pydantic import BaseModel, Field -from loguru import logger - -from coagent.llm_models.llm_config import EmbedConfig, LLMConfig -from .base_tool import BaseToolModel - -from coagent.service.cb_api import search_code, search_related_vertices, search_code_by_vertex - - -# 问题进来 -# 调用函数 0:输入问题,输出代码文件名 1 和 代码文件 1 -# -# agent 1 -# 1. LLM:代码+问题 输出:是否能解决 -# -# agent 2 -# 1. 调用函数 1 :输入:代码文件名 1 输出:代码文件名列表 -# 2. LLM:输入代码文件 1, 问题,代码文件名列表,输出:代码文件名 2 -# 3. 调用函数 2: 输入 :代码文件名 2 输出:代码文件 2 - - -class CodeRetrievalSingle(BaseToolModel): - name = "CodeRetrievalOneCode" - description = "输入用户的问题,输出一个代码文件名和代码文件" - - class ToolInputArgs(BaseModel): - query: str = Field(..., description="检索的问题") - code_base_name: str = Field(..., description="代码库名称", examples=["samples"]) - - class ToolOutputArgs(BaseModel): - """Output for MetricsQuery.""" - code: str = Field(..., description="检索代码") - vertex: str = Field(..., description="代码对应 id") - - @classmethod - def run(cls, code_base_name, query, embed_config: EmbedConfig, llm_config: LLMConfig, search_type="description", **kargs): - """excute your tool!""" - - code_limit = 1 - - # default - search_result = search_code(code_base_name, query, code_limit, search_type=search_type, - history_node_list=[], - embed_engine=embed_config.embed_engine, embed_model=embed_config.embed_model, embed_model_path=embed_config.embed_model_path, - model_device=embed_config.model_device, model_name=llm_config.model_name, temperature=llm_config.temperature, - api_base_url=llm_config.api_base_url, api_key=llm_config.api_key, embed_config=embed_config, use_nh=kargs.get("use_nh", True), - local_graph_path=kargs.get("local_graph_path", "") - ) - if os.environ.get("log_verbose", "0") >= "3": - logger.debug(search_result) - code = search_result['context'] - vertex = search_result['related_vertices'][0] - # logger.debug(f"code: {code}, vertex: {vertex}") - - res = { - 'code': code, - 'vertex': vertex - } - - return res - - -class RelatedVerticesRetrival(BaseToolModel): - name = "RelatedVerticesRetrival" - description = "输入代码节点名,返回相连的节点名" - - class ToolInputArgs(BaseModel): - code_base_name: str = Field(..., description="代码库名称", examples=["samples"]) - vertex: str = Field(..., description="节点名", examples=["samples"]) - - class ToolOutputArgs(BaseModel): - """Output for MetricsQuery.""" - vertices: list = Field(..., description="相连节点名") - - @classmethod - def run(cls, code_base_name: str, vertex: str, **kargs): - """execute your tool!""" - related_vertices = search_related_vertices(cb_name=code_base_name, vertex=vertex) - # logger.debug(f"related_vertices: {related_vertices}") - - return related_vertices - - -class Vertex2Code(BaseToolModel): - name = "Vertex2Code" - description = "输入代码节点名,返回对应的代码文件" - - class ToolInputArgs(BaseModel): - code_base_name: str = Field(..., description="代码库名称", examples=["samples"]) - vertex: str = Field(..., description="节点名", examples=["samples"]) - - class ToolOutputArgs(BaseModel): - """Output for MetricsQuery.""" - code: str = Field(..., description="代码名") - - @classmethod - def run(cls, code_base_name: str, vertex: str, **kargs): - """execute your tool!""" - # format vertex - if ',' in vertex: - vertex_list = vertex.split(',') - vertex = vertex_list[0].strip(' "') - else: - vertex = vertex.strip(' "') - - # logger.info(f'vertex={vertex}') - res = search_code_by_vertex(cb_name=code_base_name, vertex=vertex) - return res \ No newline at end of file diff --git a/coagent/tools/docs_retrieval.py b/coagent/tools/docs_retrieval.py deleted file mode 100644 index fcb7163..0000000 --- a/coagent/tools/docs_retrieval.py +++ /dev/null @@ -1,39 +0,0 @@ -from pydantic import BaseModel, Field -from loguru import logger - -from coagent.llm_models.llm_config import EmbedConfig -from .base_tool import BaseToolModel -from coagent.service.kb_api import search_docs - - -class DocRetrieval(BaseToolModel): - name = "DocRetrieval" - description = "采用向量化对本地知识库进行检索" - - class ToolInputArgs(BaseModel): - query: str = Field(..., description="检索的关键字或问题") - knowledge_base_name: str = Field(..., description="知识库名称", examples=["samples"]) - search_top: int = Field(5, description="检索返回的数量") - score_threshold: float = Field(1.0, description="知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右", ge=0, le=1) - - class ToolOutputArgs(BaseModel): - """Output for MetricsQuery.""" - title: str = Field(..., description="检索网页标题") - snippet: str = Field(..., description="检索内容的判断") - link: str = Field(..., description="检索网页地址") - - @classmethod - def run(cls, query, knowledge_base_name, search_top=5, score_threshold=1.0, embed_config: EmbedConfig=EmbedConfig(), kb_root_path: str=""): - """excute your tool!""" - try: - docs = search_docs(query, knowledge_base_name, search_top, score_threshold, - kb_root_path=kb_root_path, embed_engine=embed_config.embed_engine, - embed_model=embed_config.embed_model, embed_model_path=embed_config.embed_model_path, - model_device=embed_config.model_device - ) - except Exception as e: - logger.exception(e) - return_docs = [] - for idx, doc in enumerate(docs): - return_docs.append({"index": idx, "snippet": doc.page_content, "title": doc.metadata.get("source"), "link": doc.metadata.get("source")}) - return return_docs diff --git a/coagent/tools/duckduckgo_search.py b/coagent/tools/duckduckgo_search.py deleted file mode 100644 index 8e079e4..0000000 --- a/coagent/tools/duckduckgo_search.py +++ /dev/null @@ -1,72 +0,0 @@ - -import json -import os -import re -from pydantic import BaseModel, Field -from typing import List, Dict -import requests -import numpy as np -from loguru import logger - -from .base_tool import BaseToolModel -try: - from duckduckgo_search import DDGS -except: - logger.warning("can't find duckduckgo_search, if you need it, please `pip install duckduckgo_search`") - - -class DDGSTool(BaseToolModel): - name = "DDGSTool" - description = "通过duckduckgo进行资料搜索" - - class ToolInputArgs(BaseModel): - query: str = Field(..., description="检索的关键字或问题") - search_top: int = Field(..., description="检索返回的数量") - region: str = Field("wt-wt", enum=["wt-wt", "us-en", "uk-en", "ru-ru"], description="搜索的区域") - safesearch: str = Field("moderate", enum=["on", "moderate", "off"], description="") - timelimit: str = Field(None, enum=[None, "d", "w", "m", "y"], description="查询时间方式") - backend: str = Field("api", description="搜索的资料来源") - - class ToolOutputArgs(BaseModel): - """Output for MetricsQuery.""" - title: str = Field(..., description="检索网页标题") - snippet: str = Field(..., description="检索内容的判断") - link: str = Field(..., description="检索网页地址") - - @classmethod - def run(cls, query, search_top, region="wt-wt", safesearch="moderate", timelimit=None, backend="api"): - """excute your tool!""" - with DDGS(proxies=os.environ.get("DUCKDUCKGO_PROXY"), timeout=20) as ddgs: - ddgs._session.headers["Referer"] = "" - results = ddgs.text( - query, - region=region, - safesearch=safesearch, - timelimit=timelimit, - backend=backend, - ) - if results is None: - return [{"Result": "No good DuckDuckGo Search Result was found"}] - - def to_metadata(result: Dict) -> Dict[str, str]: - if backend == "news": - return { - "date": result["date"], - "title": result["title"], - "snippet": result["body"], - "source": result["source"], - "link": result["url"], - } - return { - "snippet": result["body"], - "title": result["title"], - "link": result["href"], - } - - formatted_results = [] - for i, res in enumerate(results, 1): - if res is not None: - formatted_results.append(to_metadata(res)) - if len(formatted_results) == search_top: - break - return formatted_results diff --git a/coagent/tools/metrics_query.py b/coagent/tools/metrics_query.py deleted file mode 100644 index c3336c7..0000000 --- a/coagent/tools/metrics_query.py +++ /dev/null @@ -1,33 +0,0 @@ - -import json -import os -import re -from pydantic import BaseModel, Field -from typing import List, Dict -import requests -import numpy as np -from loguru import logger - -from .base_tool import BaseToolModel - - - -class MetricsQuery(BaseToolModel): - name = "MetricsQuery" - description = "查询机器的监控数据" - - class ToolInputArgs(BaseModel): - machine_ip: str = Field(..., description="machine_ip") - time: int = Field(..., description="time period") - - class ToolOutputArgs(BaseModel): - """Output for MetricsQuery.""" - - datas: List[float] = Field(..., description="监控时序数组") - - def run(machine_ip, time): - """excute your tool!""" - data = [0.857, 2.345, 1.234, 4.567, 3.456, 9.876, 5.678, 7.890, 6.789, 8.901, 10.987, 12.345, 11.234, 14.567, 13.456, 19.876, 15.678, 17.890, - 16.789, 18.901, 20.987, 22.345, 21.234, 24.567, 23.456, 29.876, 25.678, 27.890, 26.789, 28.901, 30.987, 32.345, 31.234, 34.567, - 33.456, 39.876, 35.678, 37.890, 36.789, 38.901, 40.987] - return data[:30] \ No newline at end of file diff --git a/coagent/tools/multiplier.py b/coagent/tools/multiplier.py deleted file mode 100644 index fee4971..0000000 --- a/coagent/tools/multiplier.py +++ /dev/null @@ -1,35 +0,0 @@ -from pydantic import BaseModel, Field -from typing import List, Dict -import requests -from loguru import logger - -from .base_tool import BaseToolModel - - - -class Multiplier(BaseToolModel): - """ - Tips: - default control Required, e.g. key1 is not Required/key2 is Required - """ - - name: str = "Multiplier" - description: str = """useful for when you need to multiply two numbers together. \ - The input to this tool should be a comma separated list of numbers of length two, representing the two numbers you want to multiply together. \ - For example, `1,2` would be the input if you wanted to multiply 1 by 2.""" - - class ToolInputArgs(BaseModel): - """Input for Multiplier.""" - - # key: str = Field(..., description="用户在高德地图官网申请web服务API类型KEY") - a: int = Field(..., description="num a") - b: int = Field(..., description="num b") - - class ToolOutputArgs(BaseModel): - """Output for Multiplier.""" - - res: int = Field(..., description="the result of two nums") - - @staticmethod - def run(a, b): - return a * b \ No newline at end of file diff --git a/coagent/tools/ocr_tool.py b/coagent/tools/ocr_tool.py deleted file mode 100644 index 49c7831..0000000 --- a/coagent/tools/ocr_tool.py +++ /dev/null @@ -1,100 +0,0 @@ -from pydantic import BaseModel, Field -from typing import List, Dict -import requests -import base64 -import urllib -import os -from loguru import logger -from .base_tool import BaseToolModel - -# from configs.model_config import JUPYTER_WORK_PATH - - -class BaiduOcrTool(BaseToolModel): - """ - Tips: - 百度ocr tool - - example: - API_KEY = "" - SECRET_KEY = "" - image_path = '' - ocr_result = BaiduOcrTool.run(API_KEY=API_KEY , SECRET_KEY=SECRET_KEY, image_path=image_path) - """ - - name: str = "Baidu_orc_tool" - description: str = """ 百度OCR手写字符识别调用器。 输入一张图片位置,返回图片中的文本""" - - class ToolInputArgs(BaseModel): - """Input for Multiplier.""" - image_name : str = Field(..., description="待提取文本信息的图片名称") - - class ToolOutputArgs(BaseModel): - """Output for Multiplier.""" - - ocr_result: str = Field(..., description="OCR分析提取的自然语言文本") - - @classmethod - def ocr_baidu_main(cls, API_KEY, SECRET_KEY, image_path): - ''' - 根据图片地址,返回OCR识别结果 - OCR的结果不仅包含了文字,也包含了文字的位置。但可以根据简单的提取方法,只将文字提前取出来 - 下面是ocr的返回结果 - '{"words_result":[{"location":{"top":17,"left":33,"width":227,"height":24},"words":"手写识别测试图片样例:"}, - {"location":{"top":91,"left":190,"width":713,"height":70},"words":"每一个人的生命中,都应该有一次,"}, - {"location":{"top":177,"left":87,"width":831,"height":65},"words":"为了某个人而忘了自己,不求有结果."}, - {"location":{"top":263,"left":80,"width":842,"height":76},"words":"不求同行,不求曾经拥有,甚至不求"}], - "words_result_num":4,"log_id":1722502064951792680}' - ''' - url = "https://aip.baidubce.com/rest/2.0/ocr/v1/handwriting?access_token=" + BaiduOcrTool.get_access_token(API_KEY, SECRET_KEY) - - # image 可以通过 get_file_content_as_base64("C:\fakepath\ocr_input_example.png",True) 方法获取 - image = BaiduOcrTool.get_file_content_as_base64(image_path, True) - payload = 'image=' + image + '&detect_direction=false&probability=false' - headers = { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Accept': 'application/json' - } - - response = requests.request("POST", url, headers=headers, data=payload) - s = "" - try: - for word_result in response.json()["words_result"]: - s += "\n" + word_result["words"] - except Exception as e: - logger.exception(e) - s = "无法识别图片内容" - return s - - @classmethod - def get_file_content_as_base64(cls, image_path, urlencoded=False): - """ - 获取文件base64编码 - :param path: 文件路径 - :param urlencoded: 是否对结果进行urlencoded - :return: base64编码信息 - """ - with open(image_path, "rb") as f: - content = base64.b64encode(f.read()).decode("utf8") - if urlencoded: - content = urllib.parse.quote_plus(content) - return content - - @classmethod - def get_access_token(cls, API_KEY, SECRET_KEY): - """ - 使用 AK,SK 生成鉴权签名(Access Token) - :return: access_token,或是None(如果错误) - """ - url = "https://aip.baidubce.com/oauth/2.0/token" - params = {"grant_type": "client_credentials", "client_id": API_KEY, "client_secret": SECRET_KEY} - return str(requests.post(url, params=params).json().get("access_token")) - - @classmethod - def run(cls, image_name, image_path="", API_KEY=os.environ.get("BAIDU_OCR_API_KEY"), SECRET_KEY=os.environ.get("BAIDU_OCR_SECRET_KEY")): - if os.environ.get("BAIDU_OCR_API_KEY") is None: - raise EnvironmentError("Environment variable 'BAIDU_OCR_API_KEY' not set") - if os.environ.get("BAIDU_OCR_SECRET_KEY") is None: - raise EnvironmentError("Environment variable 'BAIDU_OCR_SECRET_KEY' not set") - image_file = os.path.join(image_path, image_name) - return cls.ocr_baidu_main(API_KEY, SECRET_KEY, image_file) \ No newline at end of file diff --git a/coagent/tools/sandbox.py b/coagent/tools/sandbox.py deleted file mode 100644 index e69de29..0000000 diff --git a/coagent/tools/stock_tool.py b/coagent/tools/stock_tool.py deleted file mode 100644 index bf39b22..0000000 --- a/coagent/tools/stock_tool.py +++ /dev/null @@ -1,189 +0,0 @@ - -import json -import os -import re -from pydantic import BaseModel, Field -from typing import List, Dict, Optional -import requests -import numpy as np -from loguru import logger - -from .base_tool import BaseToolModel -from coagent.utils.common_utils import read_json_file - -from .tool_datas.stock_data import stock_infos -# cur_dir = os.path.dirname(os.path.abspath(__file__)) -# stock_infos = read_json_file(os.path.join(cur_dir, "tool_datas/stock.json")) -stock_dict = {i["mc"]: i["jys"]+i["dm"] for i in stock_infos} - - -class StockName(BaseToolModel): - """ - Tips - """ - name: str = "StockName" - description: str = "通过股票名称查询股票代码" - - class ToolInputArgs(BaseModel): - """Input for StockName""" - stock_name: int = Field(..., description="股票名称") - - class ToolOutputArgs(BaseModel): - """Output for StockName""" - stock_code: str = Field(..., description="股票代码") - - @staticmethod - def run(stock_name: str): - return stock_dict.get(stock_name, "no stock_code") - - - -class StockInfo(BaseToolModel): - """ - 用于查询股票市场数据的StockInfo工具。 - """ - - name: str = "StockInfo" - description: str = "根据提供的股票代码、日期范围和数据频率提供股票市场数据。" - - class ToolInputArgs(BaseModel): - """StockInfo的输入参数。""" - code: str = Field(..., description="要查询的股票代码,格式为'marketcode'") - end_date: Optional[str] = Field(default="", description="数据查询的结束日期。留空则为当前日期。如果没有提供结束日期,就留空") - count: int = Field(default=10, description="返回数据点的数量。") - frequency: str = Field(default='1d', description="数据点的频率,例如,'1d'表示每日,'1w'表示每周,'1M'表示每月,'1m'表示每分钟等。") - - class ToolOutputArgs(BaseModel): - """StockInfo的输出参数。""" - data: dict = Field(default=None, description="查询到的股票市场数据。") - - @staticmethod - def run(code: str, count: int, frequency: str, end_date: Optional[str]="") -> "ToolOutputArgs": - """执行股票数据查询工具。""" - # 该方法封装了调用底层股票数据API的逻辑,并将结果格式化为pandas DataFrame。 - try: - df = get_price(code, end_date=end_date, count=count, frequency=frequency) - # 将DataFrame转换为输出的字典格式 - data = df.reset_index().to_dict(orient='list') # 将dataframe转换为字典列表 - return StockInfo.ToolOutputArgs(data=data) - except Exception as e: - logger.exception("获取股票数据时发生错误。") - return e - -#-*- coding:utf-8 -*- --------------Ashare 股票行情数据双核心版( https://github.com/mpquant/Ashare ) - -import json,requests,datetime -import pandas as pd # - -#腾讯日线 - -def get_price_day_tx(code, end_date='', count=10, frequency='1d'): #日线获取 - - unit='week' if frequency in '1w' else 'month' if frequency in '1M' else 'day' #判断日线,周线,月线 - - if end_date: end_date=end_date.strftime('%Y-%m-%d') if isinstance(end_date,datetime.date) else end_date.split(' ')[0] - - end_date='' if end_date==datetime.datetime.now().strftime('%Y-%m-%d') else end_date #如果日期今天就变成空 - - URL=f'http://web.ifzq.gtimg.cn/appstock/app/fqkline/get?param={code},{unit},,{end_date},{count},qfq' - - st= json.loads(requests.get(URL).content); ms='qfq'+unit; stk=st['data'][code] - - buf=stk[ms] if ms in stk else stk[unit] #指数返回不是qfqday,是day - - df=pd.DataFrame(buf,columns=['time','open','close','high','low','volume'],dtype='float') - - df.time=pd.to_datetime(df.time); df.set_index(['time'], inplace=True); df.index.name='' #处理索引 - - return df - - -#腾讯分钟线 - -def get_price_min_tx(code, end_date=None, count=10, frequency='1d'): #分钟线获取 - - ts=int(frequency[:-1]) if frequency[:-1].isdigit() else 1 #解析K线周期数 - - if end_date: end_date=end_date.strftime('%Y-%m-%d') if isinstance(end_date,datetime.date) else end_date.split(' ')[0] - - URL=f'http://ifzq.gtimg.cn/appstock/app/kline/mkline?param={code},m{ts},,{count}' - - st= json.loads(requests.get(URL).content); buf=st[ 'data'][code]['m'+str(ts)] - - df=pd.DataFrame(buf,columns=['time','open','close','high','low','volume','n1','n2']) - - df=df[['time','open','close','high','low','volume']] - - df[['open','close','high','low','volume']]=df[['open','close','high','low','volume']].astype('float') - - df.time=pd.to_datetime(df.time); df.set_index(['time'], inplace=True); df.index.name='' #处理索引 - - df['close'][-1]=float(st['data'][code]['qt'][code][3]) #最新基金数据是3位的 - - return df - - -#sina新浪全周期获取函数,分钟线 5m,15m,30m,60m 日线1d=240m 周线1w=1200m 1月=7200m - -def get_price_sina(code, end_date='', count=10, frequency='60m'): #新浪全周期获取函数 - - frequency=frequency.replace('1d','240m').replace('1w','1200m').replace('1M','7200m'); mcount=count - - ts=int(frequency[:-1]) if frequency[:-1].isdigit() else 1 #解析K线周期数 - - if (end_date!='') & (frequency in ['240m','1200m','7200m']): - - end_date=pd.to_datetime(end_date) if not isinstance(end_date,datetime.date) else end_date #转换成datetime - unit=4 if frequency=='1200m' else 29 if frequency=='7200m' else 1 #4,29多几个数据不影响速度 - - count=count+(datetime.datetime.now()-end_date).days//unit #结束时间到今天有多少天自然日(肯定 >交易日) - - #print(code,end_date,count) - - URL=f'http://money.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_MarketData.getKLineData?symbol={code}&scale={ts}&ma=5&datalen={count}' - - dstr= json.loads(requests.get(URL).content); - - #df=pd.DataFrame(dstr,columns=['day','open','high','low','close','volume'],dtype='float') - - df= pd.DataFrame(dstr,columns=['day','open','high','low','close','volume']) - - df['open'] = df['open'].astype(float); df['high'] = df['high'].astype(float); #转换数据类型 - df['low'] = df['low'].astype(float); df['close'] = df['close'].astype(float); df['volume'] = df['volume'].astype(float) - - df.day=pd.to_datetime(df.day); - - df.set_index(['day'], inplace=True); - - df.index.name='' #处理索引 - - if (end_date!='') & (frequency in ['240m','1200m','7200m']): - return df[df.index<=end_date][-mcount:] #日线带结束时间先返回 - - return df - - -def get_price(code, end_date='',count=10, frequency='1d', fields=[]): #对外暴露只有唯一函数,这样对用户才是最友好的 - - xcode= code.replace('.XSHG','').replace('.XSHE','') #证券代码编码兼容处理 - xcode='sh'+xcode if ('XSHG' in code) else 'sz'+xcode if ('XSHE' in code) else code - - if frequency in ['1d','1w','1M']: #1d日线 1w周线 1M月线 - try: - return get_price_sina( xcode, end_date=end_date,count=count,frequency=frequency) #主力 - except: - return get_price_day_tx(xcode,end_date=end_date,count=count,frequency=frequency) #备用 - - if frequency in ['1m','5m','15m','30m','60m']: #分钟线 ,1m只有腾讯接口 5分钟5m 60分钟60m - if frequency in '1m': - return get_price_min_tx(xcode,end_date=end_date,count=count,frequency=frequency) - try: - return get_price_sina(xcode,end_date=end_date,count=count,frequency=frequency) #主力 - except: - return get_price_min_tx(xcode,end_date=end_date,count=count,frequency=frequency) #备用 - - -if __name__ == "__main__": - tool = StockInfo() - output = tool.run(code='sh600519', end_date='', count=10, frequency='15m') - print(output.json(indent=2)) \ No newline at end of file diff --git a/coagent/tools/tool_datas/__init__.py b/coagent/tools/tool_datas/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/coagent/tools/tool_datas/stock_data.py b/coagent/tools/tool_datas/stock_data.py deleted file mode 100644 index 8a3197c..0000000 --- a/coagent/tools/tool_datas/stock_data.py +++ /dev/null @@ -1 +0,0 @@ -stock_infos = [{"dm":"603062","mc":"N麦加","jys":"sh"},{"dm":"300507","mc":"苏奥传感","jys":"sz"},{"dm":"301202","mc":"朗威股份","jys":"sz"},{"dm":"300951","mc":"博硕科技","jys":"sz"},{"dm":"300608","mc":"思特奇","jys":"sz"},{"dm":"300063","mc":"天龙集团","jys":"sz"},{"dm":"300502","mc":"新易盛","jys":"sz"},{"dm":"300210","mc":"森远股份","jys":"sz"},{"dm":"300364","mc":"中文在线","jys":"sz"},{"dm":"688577","mc":"浙海德曼","jys":"sh"},{"dm":"300913","mc":"兆龙互连","jys":"sz"},{"dm":"300522","mc":"世名科技","jys":"sz"},{"dm":"301312","mc":"智立方","jys":"sz"},{"dm":"300480","mc":"光力科技","jys":"sz"},{"dm":"301191","mc":"菲菱科思","jys":"sz"},{"dm":"300551","mc":"古鳌科技","jys":"sz"},{"dm":"688629","mc":"华丰科技","jys":"sh"},{"dm":"688048","mc":"长光华芯","jys":"sh"},{"dm":"300883","mc":"龙利得","jys":"sz"},{"dm":"688031","mc":"星环科技-U","jys":"sh"},{"dm":"300684","mc":"中石科技","jys":"sz"},{"dm":"300570","mc":"太辰光","jys":"sz"},{"dm":"300694","mc":"蠡湖股份","jys":"sz"},{"dm":"000677","mc":"恒天海龙","jys":"sz"},{"dm":"601901","mc":"方正证券","jys":"sh"},{"dm":"002238","mc":"天威视讯","jys":"sz"},{"dm":"000829","mc":"天音控股","jys":"sz"},{"dm":"002712","mc":"思美传媒","jys":"sz"},{"dm":"600892","mc":"大晟文化","jys":"sh"},{"dm":"600592","mc":"龙溪股份","jys":"sh"},{"dm":"000056","mc":"皇庭国际","jys":"sz"},{"dm":"600630","mc":"龙头股份","jys":"sh"},{"dm":"603050","mc":"科林电气","jys":"sh"},{"dm":"603726","mc":"朗迪集团","jys":"sh"},{"dm":"600360","mc":"华微电子","jys":"sh"},{"dm":"002055","mc":"得润电子","jys":"sz"},{"dm":"001238","mc":"浙江正特","jys":"sz"},{"dm":"603729","mc":"龙韵股份","jys":"sh"},{"dm":"688328","mc":"深科达","jys":"sh"},{"dm":"603305","mc":"旭升集团","jys":"sh"},{"dm":"002222","mc":"福晶科技","jys":"sz"},{"dm":"603220","mc":"中贝通信","jys":"sh"},{"dm":"002615","mc":"哈尔斯","jys":"sz"},{"dm":"002889","mc":"东方嘉盛","jys":"sz"},{"dm":"000628","mc":"高新发展","jys":"sz"},{"dm":"600520","mc":"文一科技","jys":"sh"},{"dm":"603380","mc":"易德龙","jys":"sh"},{"dm":"605218","mc":"伟时电子","jys":"sh"},{"dm":"600692","mc":"亚通股份","jys":"sh"},{"dm":"600222","mc":"太龙药业","jys":"sh"},{"dm":"002981","mc":"朝阳科技","jys":"sz"},{"dm":"002692","mc":"远程股份","jys":"sz"},{"dm":"002313","mc":"日海智能","jys":"sz"},{"dm":"000026","mc":"飞亚达","jys":"sz"},{"dm":"603985","mc":"恒润股份","jys":"sh"},{"dm":"002229","mc":"鸿博股份","jys":"sz"},{"dm":"603266","mc":"天龙股份","jys":"sh"},{"dm":"000815","mc":"美利云","jys":"sz"},{"dm":"002988","mc":"豪美新材","jys":"sz"},{"dm":"600679","mc":"上海凤凰","jys":"sh"},{"dm":"002931","mc":"锋龙股份","jys":"sz"},{"dm":"603037","mc":"凯众股份","jys":"sh"},{"dm":"000828","mc":"东莞控股","jys":"sz"},{"dm":"605255","mc":"天普股份","jys":"sh"},{"dm":"603667","mc":"五洲新春","jys":"sh"},{"dm":"003015","mc":"日久光电","jys":"sz"},{"dm":"603003","mc":"龙宇股份","jys":"sh"},{"dm":"603390","mc":"通达电气","jys":"sh"},{"dm":"600816","mc":"建元信托","jys":"sh"},{"dm":"600331","mc":"宏达股份","jys":"sh"},{"dm":"002682","mc":"龙洲股份","jys":"sz"},{"dm":"688010","mc":"福光股份","jys":"sh"},{"dm":"300629","mc":"新劲刚","jys":"sz"},{"dm":"605577","mc":"龙版传媒","jys":"sh"},{"dm":"300264","mc":"佳创视讯","jys":"sz"},{"dm":"300475","mc":"香农芯创","jys":"sz"},{"dm":"688498","mc":"源杰科技","jys":"sh"},{"dm":"600131","mc":"国网信通","jys":"sh"},{"dm":"300757","mc":"罗博特科","jys":"sz"},{"dm":"300308","mc":"中际旭创","jys":"sz"},{"dm":"300657","mc":"弘信电子","jys":"sz"},{"dm":"300088","mc":"长信科技","jys":"sz"},{"dm":"300807","mc":"天迈科技","jys":"sz"},{"dm":"600133","mc":"东湖高新","jys":"sh"},{"dm":"688662","mc":"富信科技","jys":"sh"},{"dm":"002993","mc":"奥海科技","jys":"sz"},{"dm":"300701","mc":"森霸传感","jys":"sz"},{"dm":"688593","mc":"新相微","jys":"sh"},{"dm":"300620","mc":"光库科技","jys":"sz"},{"dm":"300272","mc":"开能健康","jys":"sz"},{"dm":"300820","mc":"英杰电气","jys":"sz"},{"dm":"300846","mc":"首都在线","jys":"sz"},{"dm":"301085","mc":"亚康股份","jys":"sz"},{"dm":"300523","mc":"辰安科技","jys":"sz"},{"dm":"300394","mc":"天孚通信","jys":"sz"},{"dm":"603353","mc":"和顺石油","jys":"sh"},{"dm":"688066","mc":"航天宏图","jys":"sh"},{"dm":"688313","mc":"仕佳光子","jys":"sh"},{"dm":"688115","mc":"思林杰","jys":"sh"},{"dm":"688141","mc":"杰华特","jys":"sh"},{"dm":"300976","mc":"达瑞电子","jys":"sz"},{"dm":"301387","mc":"光大同创","jys":"sz"},{"dm":"603629","mc":"利通电子","jys":"sh"},{"dm":"301360","mc":"荣旗科技","jys":"sz"},{"dm":"002929","mc":"润建股份","jys":"sz"},{"dm":"300211","mc":"亿通科技","jys":"sz"},{"dm":"300781","mc":"因赛集团","jys":"sz"},{"dm":"300293","mc":"蓝英装备","jys":"sz"},{"dm":"301489","mc":"思泉新材","jys":"sz"},{"dm":"300798","mc":"锦鸡股份","jys":"sz"},{"dm":"600159","mc":"大龙地产","jys":"sh"},{"dm":"002515","mc":"金字火腿","jys":"sz"},{"dm":"300101","mc":"振芯科技","jys":"sz"},{"dm":"301486","mc":"致尚科技","jys":"sz"},{"dm":"000925","mc":"众合科技","jys":"sz"},{"dm":"300032","mc":"金龙机电","jys":"sz"},{"dm":"301389","mc":"隆扬电子","jys":"sz"},{"dm":"603496","mc":"恒为科技","jys":"sh"},{"dm":"300691","mc":"联合光电","jys":"sz"},{"dm":"002281","mc":"光迅科技","jys":"sz"},{"dm":"601599","mc":"浙文影业","jys":"sh"},{"dm":"688167","mc":"炬光科技","jys":"sh"},{"dm":"301421","mc":"波长光电","jys":"sz"},{"dm":"002902","mc":"铭普光磁","jys":"sz"},{"dm":"603779","mc":"威龙股份","jys":"sh"},{"dm":"300678","mc":"中科信息","jys":"sz"},{"dm":"300912","mc":"凯龙高科","jys":"sz"},{"dm":"600071","mc":"凤凰光学","jys":"sh"},{"dm":"301262","mc":"海看股份","jys":"sz"},{"dm":"601595","mc":"上海电影","jys":"sh"},{"dm":"688668","mc":"鼎通科技","jys":"sh"},{"dm":"300603","mc":"立昂技术","jys":"sz"},{"dm":"688280","mc":"精进电动-UW","jys":"sh"},{"dm":"300025","mc":"华星创业","jys":"sz"},{"dm":"603626","mc":"科森科技","jys":"sh"},{"dm":"688311","mc":"盟升电子","jys":"sh"},{"dm":"688159","mc":"有方科技","jys":"sh"},{"dm":"300939","mc":"秋田微","jys":"sz"},{"dm":"605086","mc":"龙高股份","jys":"sh"},{"dm":"301205","mc":"联特科技","jys":"sz"},{"dm":"002771","mc":"真视通","jys":"sz"},{"dm":"688039","mc":"当虹科技","jys":"sh"},{"dm":"688249","mc":"晶合集成","jys":"sh"},{"dm":"688683","mc":"莱尔科技","jys":"sh"},{"dm":"301015","mc":"百洋医药","jys":"sz"},{"dm":"300742","mc":"*ST越博","jys":"sz"},{"dm":"603322","mc":"超讯通信","jys":"sh"},{"dm":"300752","mc":"隆利科技","jys":"sz"},{"dm":"301297","mc":"富乐德","jys":"sz"},{"dm":"300878","mc":"维康药业","jys":"sz"},{"dm":"300400","mc":"劲拓股份","jys":"sz"},{"dm":"688337","mc":"普源精电","jys":"sh"},{"dm":"600552","mc":"凯盛科技","jys":"sh"},{"dm":"603912","mc":"佳力图","jys":"sh"},{"dm":"301321","mc":"翰博高新","jys":"sz"},{"dm":"600211","mc":"西藏药业","jys":"sh"},{"dm":"300631","mc":"久吾高科","jys":"sz"},{"dm":"002876","mc":"三利谱","jys":"sz"},{"dm":"301298","mc":"东利机械","jys":"sz"},{"dm":"603863","mc":"松炀资源","jys":"sh"},{"dm":"601059","mc":"信达证券","jys":"sh"},{"dm":"301128","mc":"强瑞技术","jys":"sz"},{"dm":"002750","mc":"龙津药业","jys":"sz"},{"dm":"688105","mc":"诺唯赞","jys":"sh"},{"dm":"688343","mc":"云天励飞-U","jys":"sh"},{"dm":"002609","mc":"捷顺科技","jys":"sz"},{"dm":"688256","mc":"寒武纪-U","jys":"sh"},{"dm":"600335","mc":"国机汽车","jys":"sh"},{"dm":"603078","mc":"江化微","jys":"sh"},{"dm":"301183","mc":"东田微","jys":"sz"},{"dm":"688097","mc":"博众精工","jys":"sh"},{"dm":"002776","mc":"*ST柏龙","jys":"sz"},{"dm":"301165","mc":"锐捷网络","jys":"sz"},{"dm":"600200","mc":"江苏吴中","jys":"sh"},{"dm":"600775","mc":"南京熊猫","jys":"sh"},{"dm":"002437","mc":"誉衡药业","jys":"sz"},{"dm":"600589","mc":"*ST榕泰","jys":"sh"},{"dm":"600265","mc":"ST景谷","jys":"sh"},{"dm":"603906","mc":"龙蟠科技","jys":"sh"},{"dm":"600326","mc":"西藏天路","jys":"sh"},{"dm":"301052","mc":"果麦文化","jys":"sz"},{"dm":"300844","mc":"山水比德","jys":"sz"},{"dm":"603186","mc":"华正新材","jys":"sh"},{"dm":"000046","mc":"*ST泛海","jys":"sz"},{"dm":"002251","mc":"*ST步高","jys":"sz"},{"dm":"002592","mc":"ST八菱","jys":"sz"},{"dm":"603603","mc":"*ST博天","jys":"sh"},{"dm":"300548","mc":"博创科技","jys":"sz"},{"dm":"301139","mc":"元道通信","jys":"sz"},{"dm":"002587","mc":"奥拓电子","jys":"sz"},{"dm":"688316","mc":"青云科技-U","jys":"sh"},{"dm":"300366","mc":"创意信息","jys":"sz"},{"dm":"300105","mc":"龙源技术","jys":"sz"},{"dm":"300506","mc":"名家汇","jys":"sz"},{"dm":"603083","mc":"剑桥科技","jys":"sh"},{"dm":"688409","mc":"富创精密","jys":"sh"},{"dm":"688698","mc":"伟创电气","jys":"sh"},{"dm":"002300","mc":"太阳电缆","jys":"sz"},{"dm":"600338","mc":"西藏珠峰","jys":"sh"},{"dm":"000068","mc":"华控赛格","jys":"sz"},{"dm":"603286","mc":"日盈电子","jys":"sh"},{"dm":"688258","mc":"卓易信息","jys":"sh"},{"dm":"300766","mc":"每日互动","jys":"sz"},{"dm":"300010","mc":"*ST豆神","jys":"sz"},{"dm":"688468","mc":"科美诊断","jys":"sh"},{"dm":"002409","mc":"雅克科技","jys":"sz"},{"dm":"600716","mc":"凤凰股份","jys":"sh"},{"dm":"600418","mc":"江淮汽车","jys":"sh"},{"dm":"002536","mc":"飞龙股份","jys":"sz"},{"dm":"688279","mc":"峰岹科技","jys":"sh"},{"dm":"300528","mc":"幸福蓝海","jys":"sz"},{"dm":"688716","mc":"中研股份","jys":"sh"},{"dm":"603466","mc":"风语筑","jys":"sh"},{"dm":"603089","mc":"正裕工业","jys":"sh"},{"dm":"600804","mc":"ST鹏博士","jys":"sh"},{"dm":"000593","mc":"德龙汇能","jys":"sz"},{"dm":"002998","mc":"优彩资源","jys":"sz"},{"dm":"688025","mc":"杰普特","jys":"sh"},{"dm":"300707","mc":"威唐工业","jys":"sz"},{"dm":"000520","mc":"凤凰航运","jys":"sz"},{"dm":"300808","mc":"久量股份","jys":"sz"},{"dm":"603238","mc":"诺邦股份","jys":"sh"},{"dm":"300841","mc":"康华生物","jys":"sz"},{"dm":"002122","mc":"汇洲智能","jys":"sz"},{"dm":"301503","mc":"智迪科技","jys":"sz"},{"dm":"600895","mc":"张江高科","jys":"sh"},{"dm":"603002","mc":"宏昌电子","jys":"sh"},{"dm":"603969","mc":"银龙股份","jys":"sh"},{"dm":"301391","mc":"卡莱特","jys":"sz"},{"dm":"605289","mc":"罗曼股份","jys":"sh"},{"dm":"300650","mc":"太龙股份","jys":"sz"},{"dm":"301313","mc":"凡拓数创","jys":"sz"},{"dm":"301013","mc":"利和兴","jys":"sz"},{"dm":"300092","mc":"科新机电","jys":"sz"},{"dm":"688456","mc":"有研粉材","jys":"sh"},{"dm":"688170","mc":"德龙激光","jys":"sh"},{"dm":"605188","mc":"国光连锁","jys":"sh"},{"dm":"300327","mc":"中颖电子","jys":"sz"},{"dm":"300379","mc":"东方通","jys":"sz"},{"dm":"603615","mc":"茶花股份","jys":"sh"},{"dm":"301070","mc":"开勒股份","jys":"sz"},{"dm":"600807","mc":"济南高新","jys":"sh"},{"dm":"002827","mc":"高争民爆","jys":"sz"},{"dm":"002802","mc":"洪汇新材","jys":"sz"},{"dm":"688182","mc":"灿勤科技","jys":"sh"},{"dm":"600493","mc":"凤竹纺织","jys":"sh"},{"dm":"688521","mc":"芯原股份","jys":"sh"},{"dm":"002845","mc":"同兴达","jys":"sz"},{"dm":"001229","mc":"魅视科技","jys":"sz"},{"dm":"300304","mc":"云意电气","jys":"sz"},{"dm":"300256","mc":"星星科技","jys":"sz"},{"dm":"300499","mc":"高澜股份","jys":"sz"},{"dm":"000034","mc":"神州数码","jys":"sz"},{"dm":"688702","mc":"盛科通信-U","jys":"sh"},{"dm":"300588","mc":"熙菱信息","jys":"sz"},{"dm":"000058","mc":"深 赛 格","jys":"sz"},{"dm":"300472","mc":"新元科技","jys":"sz"},{"dm":"300552","mc":"万集科技","jys":"sz"},{"dm":"300302","mc":"同有科技","jys":"sz"},{"dm":"002338","mc":"奥普光电","jys":"sz"},{"dm":"002657","mc":"中科金财","jys":"sz"},{"dm":"000550","mc":"江铃汽车","jys":"sz"},{"dm":"300835","mc":"龙磁科技","jys":"sz"},{"dm":"688007","mc":"光峰科技","jys":"sh"},{"dm":"600602","mc":"云赛智联","jys":"sh"},{"dm":"688218","mc":"江苏北人","jys":"sh"},{"dm":"002106","mc":"莱宝高科","jys":"sz"},{"dm":"300442","mc":"润泽科技","jys":"sz"},{"dm":"002564","mc":"*ST天沃","jys":"sz"},{"dm":"002432","mc":"九安医疗","jys":"sz"},{"dm":"300870","mc":"欧陆通","jys":"sz"},{"dm":"601886","mc":"江河集团","jys":"sh"},{"dm":"002476","mc":"宝莫股份","jys":"sz"},{"dm":"688502","mc":"茂莱光学","jys":"sh"},{"dm":"300236","mc":"上海新阳","jys":"sz"},{"dm":"600987","mc":"航民股份","jys":"sh"},{"dm":"000625","mc":"长安汽车","jys":"sz"},{"dm":"002548","mc":"金新农","jys":"sz"},{"dm":"301185","mc":"鸥玛软件","jys":"sz"},{"dm":"000045","mc":"深纺织A","jys":"sz"},{"dm":"300331","mc":"苏大维格","jys":"sz"},{"dm":"603042","mc":"华脉科技","jys":"sh"},{"dm":"301268","mc":"铭利达","jys":"sz"},{"dm":"002322","mc":"理工能科","jys":"sz"},{"dm":"600449","mc":"宁夏建材","jys":"sh"},{"dm":"688111","mc":"金山办公","jys":"sh"},{"dm":"002217","mc":"合力泰","jys":"sz"},{"dm":"688426","mc":"康为世纪","jys":"sh"},{"dm":"300589","mc":"江龙船艇","jys":"sz"},{"dm":"688418","mc":"震有科技","jys":"sh"},{"dm":"300533","mc":"冰川网络","jys":"sz"},{"dm":"002947","mc":"恒铭达","jys":"sz"},{"dm":"688071","mc":"华依科技","jys":"sh"},{"dm":"002847","mc":"盐津铺子","jys":"sz"},{"dm":"600619","mc":"海立股份","jys":"sh"},{"dm":"000988","mc":"华工科技","jys":"sz"},{"dm":"600839","mc":"四川长虹","jys":"sh"},{"dm":"688205","mc":"德科立","jys":"sh"},{"dm":"300555","mc":"ST路通","jys":"sz"},{"dm":"300843","mc":"胜蓝股份","jys":"sz"},{"dm":"603203","mc":"快克智能","jys":"sh"},{"dm":"300921","mc":"南凌科技","jys":"sz"},{"dm":"603005","mc":"晶方科技","jys":"sh"},{"dm":"688501","mc":"青达环保","jys":"sh"},{"dm":"000050","mc":"深天马A","jys":"sz"},{"dm":"300780","mc":"德恩精工","jys":"sz"},{"dm":"600666","mc":"ST瑞德","jys":"sh"},{"dm":"301131","mc":"聚赛龙","jys":"sz"},{"dm":"300135","mc":"宝利国际","jys":"sz"},{"dm":"002152","mc":"广电运通","jys":"sz"},{"dm":"002602","mc":"世纪华通","jys":"sz"},{"dm":"601001","mc":"晋控煤业","jys":"sh"},{"dm":"688078","mc":"龙软科技","jys":"sh"},{"dm":"300218","mc":"安利股份","jys":"sz"},{"dm":"000712","mc":"锦龙股份","jys":"sz"},{"dm":"300654","mc":"世纪天鸿","jys":"sz"},{"dm":"000809","mc":"铁岭新城","jys":"sz"},{"dm":"688055","mc":"龙腾光电","jys":"sh"},{"dm":"300337","mc":"银邦股份","jys":"sz"},{"dm":"300687","mc":"赛意信息","jys":"sz"},{"dm":"601099","mc":"太平洋","jys":"sh"},{"dm":"001380","mc":"华纬科技","jys":"sz"},{"dm":"301383","mc":"天键股份","jys":"sz"},{"dm":"688609","mc":"九联科技","jys":"sh"},{"dm":"688486","mc":"龙迅股份","jys":"sh"},{"dm":"002428","mc":"云南锗业","jys":"sz"},{"dm":"688515","mc":"裕太微-U","jys":"sh"},{"dm":"600853","mc":"龙建股份","jys":"sh"},{"dm":"300128","mc":"锦富技术","jys":"sz"},{"dm":"688229","mc":"博睿数据","jys":"sh"},{"dm":"688047","mc":"龙芯中科","jys":"sh"},{"dm":"688158","mc":"优刻得-W","jys":"sh"},{"dm":"603283","mc":"赛腾股份","jys":"sh"},{"dm":"603019","mc":"中科曙光","jys":"sh"},{"dm":"603918","mc":"金桥信息","jys":"sh"},{"dm":"000936","mc":"华西股份","jys":"sz"},{"dm":"002882","mc":"金龙羽","jys":"sz"},{"dm":"688183","mc":"生益电子","jys":"sh"},{"dm":"688143","mc":"长盈通","jys":"sh"},{"dm":"300637","mc":"扬帆新材","jys":"sz"},{"dm":"603605","mc":"珀莱雅","jys":"sh"},{"dm":"300179","mc":"四方达","jys":"sz"},{"dm":"300249","mc":"依米康","jys":"sz"},{"dm":"000009","mc":"中国宝安","jys":"sz"},{"dm":"301419","mc":"阿莱德","jys":"sz"},{"dm":"301110","mc":"青木股份","jys":"sz"},{"dm":"600410","mc":"华胜天成","jys":"sh"},{"dm":"688286","mc":"敏芯股份","jys":"sh"},{"dm":"001336","mc":"楚环科技","jys":"sz"},{"dm":"002456","mc":"欧菲光","jys":"sz"},{"dm":"002178","mc":"延华智能","jys":"sz"},{"dm":"000010","mc":"美丽生态","jys":"sz"},{"dm":"002261","mc":"拓维信息","jys":"sz"},{"dm":"002555","mc":"三七互娱","jys":"sz"},{"dm":"300909","mc":"汇创达","jys":"sz"},{"dm":"002855","mc":"捷荣技术","jys":"sz"},{"dm":"603332","mc":"苏州龙杰","jys":"sh"},{"dm":"300074","mc":"华平股份","jys":"sz"},{"dm":"688041","mc":"海光信息","jys":"sh"},{"dm":"002795","mc":"永和智控","jys":"sz"},{"dm":"002962","mc":"五方光电","jys":"sz"},{"dm":"001314","mc":"亿道信息","jys":"sz"},{"dm":"300166","mc":"东方国信","jys":"sz"},{"dm":"688687","mc":"凯因科技","jys":"sh"},{"dm":"002864","mc":"盘龙药业","jys":"sz"},{"dm":"603988","mc":"中电电机","jys":"sh"},{"dm":"301021","mc":"英诺激光","jys":"sz"},{"dm":"000889","mc":"ST中嘉","jys":"sz"},{"dm":"002760","mc":"凤形股份","jys":"sz"},{"dm":"300790","mc":"宇瞳光学","jys":"sz"},{"dm":"605008","mc":"长鸿高科","jys":"sh"},{"dm":"300397","mc":"天和防务","jys":"sz"},{"dm":"603970","mc":"中农立华","jys":"sh"},{"dm":"688639","mc":"华恒生物","jys":"sh"},{"dm":"300727","mc":"润禾材料","jys":"sz"},{"dm":"601028","mc":"玉龙股份","jys":"sh"},{"dm":"301151","mc":"冠龙节能","jys":"sz"},{"dm":"688298","mc":"东方生物","jys":"sh"},{"dm":"600100","mc":"同方股份","jys":"sh"},{"dm":"001288","mc":"运机集团","jys":"sz"},{"dm":"605588","mc":"冠石科技","jys":"sh"},{"dm":"300316","mc":"晶盛机电","jys":"sz"},{"dm":"600498","mc":"烽火通信","jys":"sh"},{"dm":"300279","mc":"和晶科技","jys":"sz"},{"dm":"688253","mc":"英诺特","jys":"sh"},{"dm":"688737","mc":"中自科技","jys":"sh"},{"dm":"300427","mc":"*ST红相","jys":"sz"},{"dm":"000955","mc":"欣龙控股","jys":"sz"},{"dm":"301558","mc":"三态股份","jys":"sz"},{"dm":"002785","mc":"万里石","jys":"sz"},{"dm":"603677","mc":"奇精机械","jys":"sh"},{"dm":"301218","mc":"华是科技","jys":"sz"},{"dm":"300822","mc":"贝仕达克","jys":"sz"},{"dm":"300819","mc":"聚杰微纤","jys":"sz"},{"dm":"688162","mc":"巨一科技","jys":"sh"},{"dm":"300667","mc":"必创科技","jys":"sz"},{"dm":"603922","mc":"金鸿顺","jys":"sh"},{"dm":"688798","mc":"艾为电子","jys":"sh"},{"dm":"301086","mc":"鸿富瀚","jys":"sz"},{"dm":"300496","mc":"中科创达","jys":"sz"},{"dm":"600975","mc":"新五丰","jys":"sh"},{"dm":"300007","mc":"汉威科技","jys":"sz"},{"dm":"300738","mc":"奥飞数据","jys":"sz"},{"dm":"000753","mc":"漳州发展","jys":"sz"},{"dm":"688027","mc":"国盾量子","jys":"sh"},{"dm":"301041","mc":"金百泽","jys":"sz"},{"dm":"300011","mc":"鼎汉技术","jys":"sz"},{"dm":"600353","mc":"旭光电子","jys":"sh"},{"dm":"300863","mc":"卡倍亿","jys":"sz"},{"dm":"600889","mc":"南京化纤","jys":"sh"},{"dm":"601138","mc":"工业富联","jys":"sh"},{"dm":"300708","mc":"聚灿光电","jys":"sz"},{"dm":"000021","mc":"深科技","jys":"sz"},{"dm":"300828","mc":"锐新科技","jys":"sz"},{"dm":"002892","mc":"科力尔","jys":"sz"},{"dm":"603579","mc":"荣泰健康","jys":"sh"},{"dm":"300160","mc":"秀强股份","jys":"sz"},{"dm":"300521","mc":"爱司凯","jys":"sz"},{"dm":"002912","mc":"中新赛克","jys":"sz"},{"dm":"002291","mc":"遥望科技","jys":"sz"},{"dm":"300956","mc":"英力股份","jys":"sz"},{"dm":"000096","mc":"广聚能源","jys":"sz"},{"dm":"002885","mc":"京泉华","jys":"sz"},{"dm":"605128","mc":"上海沿浦","jys":"sh"},{"dm":"300576","mc":"容大感光","jys":"sz"},{"dm":"300383","mc":"光环新网","jys":"sz"},{"dm":"300001","mc":"特锐德","jys":"sz"},{"dm":"300029","mc":"ST天龙","jys":"sz"},{"dm":"688525","mc":"佰维存储","jys":"sh"},{"dm":"600491","mc":"龙元建设","jys":"sh"},{"dm":"600766","mc":"*ST园城","jys":"sh"},{"dm":"300893","mc":"松原股份","jys":"sz"},{"dm":"603111","mc":"康尼机电","jys":"sh"},{"dm":"000509","mc":"华塑控股","jys":"sz"},{"dm":"688269","mc":"凯立新材","jys":"sh"},{"dm":"002279","mc":"久其软件","jys":"sz"},{"dm":"002387","mc":"维信诺","jys":"sz"},{"dm":"300994","mc":"久祺股份","jys":"sz"},{"dm":"000818","mc":"航锦科技","jys":"sz"},{"dm":"688129","mc":"东来技术","jys":"sh"},{"dm":"688114","mc":"华大智造","jys":"sh"},{"dm":"000570","mc":"苏常柴A","jys":"sz"},{"dm":"601456","mc":"国联证券","jys":"sh"},{"dm":"301338","mc":"凯格精机","jys":"sz"},{"dm":"688259","mc":"创耀科技","jys":"sh"},{"dm":"688606","mc":"奥泰生物","jys":"sh"},{"dm":"301036","mc":"双乐股份","jys":"sz"},{"dm":"300998","mc":"宁波方正","jys":"sz"},{"dm":"688578","mc":"艾力斯","jys":"sh"},{"dm":"688362","mc":"甬矽电子","jys":"sh"},{"dm":"003019","mc":"宸展光电","jys":"sz"},{"dm":"300992","mc":"泰福泵业","jys":"sz"},{"dm":"301396","mc":"宏景科技","jys":"sz"},{"dm":"600114","mc":"东睦股份","jys":"sh"},{"dm":"688520","mc":"神州细胞-U","jys":"sh"},{"dm":"002881","mc":"美格智能","jys":"sz"},{"dm":"603013","mc":"亚普股份","jys":"sh"},{"dm":"000637","mc":"ST实华","jys":"sz"},{"dm":"300941","mc":"创识科技","jys":"sz"},{"dm":"688387","mc":"信科移动-U","jys":"sh"},{"dm":"603398","mc":"沐邦高科","jys":"sh"},{"dm":"603598","mc":"引力传媒","jys":"sh"},{"dm":"600661","mc":"昂立教育","jys":"sh"},{"dm":"600237","mc":"铜峰电子","jys":"sh"},{"dm":"301169","mc":"零点有数","jys":"sz"},{"dm":"300597","mc":"吉大通信","jys":"sz"},{"dm":"002607","mc":"中公教育","jys":"sz"},{"dm":"300303","mc":"聚飞光电","jys":"sz"},{"dm":"002726","mc":"龙大美食","jys":"sz"},{"dm":"688095","mc":"福昕软件","jys":"sh"},{"dm":"300796","mc":"贝斯美","jys":"sz"},{"dm":"300799","mc":"*ST左江","jys":"sz"},{"dm":"300945","mc":"曼卡龙","jys":"sz"},{"dm":"002086","mc":"*ST东洋","jys":"sz"},{"dm":"688416","mc":"恒烁股份","jys":"sh"},{"dm":"600388","mc":"龙净环保","jys":"sh"},{"dm":"603160","mc":"汇顶科技","jys":"sh"},{"dm":"002199","mc":"东晶电子","jys":"sz"},{"dm":"000938","mc":"紫光股份","jys":"sz"},{"dm":"600867","mc":"通化东宝","jys":"sh"},{"dm":"301381","mc":"赛维时代","jys":"sz"},{"dm":"605050","mc":"福然德","jys":"sh"},{"dm":"000608","mc":"阳光股份","jys":"sz"},{"dm":"300711","mc":"广哈通信","jys":"sz"},{"dm":"688292","mc":"浩瀚深度","jys":"sh"},{"dm":"300429","mc":"强力新材","jys":"sz"},{"dm":"002228","mc":"合兴包装","jys":"sz"},{"dm":"688310","mc":"迈得医疗","jys":"sh"},{"dm":"002895","mc":"川恒股份","jys":"sz"},{"dm":"603887","mc":"城地香江","jys":"sh"},{"dm":"300255","mc":"常山药业","jys":"sz"},{"dm":"603648","mc":"畅联股份","jys":"sh"},{"dm":"300171","mc":"东富龙","jys":"sz"},{"dm":"300274","mc":"阳光电源","jys":"sz"},{"dm":"301133","mc":"金钟股份","jys":"sz"},{"dm":"603236","mc":"移远通信","jys":"sh"},{"dm":"300763","mc":"锦浪科技","jys":"sz"},{"dm":"003021","mc":"兆威机电","jys":"sz"},{"dm":"300076","mc":"GQY视讯","jys":"sz"},{"dm":"300643","mc":"万通智控","jys":"sz"},{"dm":"603118","mc":"共进股份","jys":"sh"},{"dm":"603773","mc":"沃格光电","jys":"sh"},{"dm":"000985","mc":"大庆华科","jys":"sz"},{"dm":"300098","mc":"高新兴","jys":"sz"},{"dm":"002797","mc":"第一创业","jys":"sz"},{"dm":"300736","mc":"百邦科技","jys":"sz"},{"dm":"300553","mc":"集智股份","jys":"sz"},{"dm":"002729","mc":"好利科技","jys":"sz"},{"dm":"300968","mc":"格林精密","jys":"sz"},{"dm":"002077","mc":"大港股份","jys":"sz"},{"dm":"300719","mc":"安达维尔","jys":"sz"},{"dm":"601555","mc":"东吴证券","jys":"sh"},{"dm":"600530","mc":"ST交昂","jys":"sh"},{"dm":"688496","mc":"清越科技","jys":"sh"},{"dm":"002725","mc":"跃岭股份","jys":"sz"},{"dm":"002312","mc":"川发龙蟒","jys":"sz"},{"dm":"300242","mc":"佳云科技","jys":"sz"},{"dm":"603725","mc":"天安新材","jys":"sh"},{"dm":"688195","mc":"腾景科技","jys":"sh"},{"dm":"300157","mc":"新锦动力","jys":"sz"},{"dm":"688273","mc":"麦澜德","jys":"sh"},{"dm":"600605","mc":"汇通能源","jys":"sh"},{"dm":"603713","mc":"密尔克卫","jys":"sh"},{"dm":"300131","mc":"英唐智控","jys":"sz"},{"dm":"000977","mc":"浪潮信息","jys":"sz"},{"dm":"600105","mc":"永鼎股份","jys":"sh"},{"dm":"603861","mc":"白云电器","jys":"sh"},{"dm":"002740","mc":"*ST爱迪","jys":"sz"},{"dm":"301326","mc":"捷邦科技","jys":"sz"},{"dm":"002674","mc":"兴业科技","jys":"sz"},{"dm":"003004","mc":"声迅股份","jys":"sz"},{"dm":"300193","mc":"佳士科技","jys":"sz"},{"dm":"300473","mc":"德尔股份","jys":"sz"},{"dm":"603306","mc":"华懋科技","jys":"sh"},{"dm":"688327","mc":"云从科技-UW","jys":"sh"},{"dm":"601666","mc":"平煤股份","jys":"sh"},{"dm":"300081","mc":"恒信东方","jys":"sz"},{"dm":"688300","mc":"联瑞新材","jys":"sh"},{"dm":"300177","mc":"中海达","jys":"sz"},{"dm":"002358","mc":"森源电气","jys":"sz"},{"dm":"300225","mc":"金力泰","jys":"sz"},{"dm":"300612","mc":"宣亚国际","jys":"sz"},{"dm":"600136","mc":"*ST明诚","jys":"sh"},{"dm":"002530","mc":"金财互联","jys":"sz"},{"dm":"688489","mc":"三未信安","jys":"sh"},{"dm":"603439","mc":"贵州三力","jys":"sh"},{"dm":"301004","mc":"嘉益股份","jys":"sz"},{"dm":"300721","mc":"怡达股份","jys":"sz"},{"dm":"301018","mc":"申菱环境","jys":"sz"},{"dm":"002463","mc":"沪电股份","jys":"sz"},{"dm":"000801","mc":"四川九洲","jys":"sz"},{"dm":"300585","mc":"奥联电子","jys":"sz"},{"dm":"301398","mc":"星源卓镁","jys":"sz"},{"dm":"002253","mc":"川大智胜","jys":"sz"},{"dm":"688079","mc":"美迪凯","jys":"sh"},{"dm":"000700","mc":"模塑科技","jys":"sz"},{"dm":"003007","mc":"直真科技","jys":"sz"},{"dm":"600173","mc":"卧龙地产","jys":"sh"},{"dm":"688102","mc":"斯瑞新材","jys":"sh"},{"dm":"301117","mc":"佳缘科技","jys":"sz"},{"dm":"002385","mc":"大北农","jys":"sz"},{"dm":"300825","mc":"阿尔特","jys":"sz"},{"dm":"002416","mc":"爱施德","jys":"sz"},{"dm":"002940","mc":"昂利康","jys":"sz"},{"dm":"000032","mc":"深桑达A","jys":"sz"},{"dm":"300812","mc":"易天股份","jys":"sz"},{"dm":"300592","mc":"华凯易佰","jys":"sz"},{"dm":"002197","mc":"证通电子","jys":"sz"},{"dm":"688382","mc":"益方生物-U","jys":"sh"},{"dm":"688787","mc":"海天瑞声","jys":"sh"},{"dm":"601188","mc":"龙江交通","jys":"sh"},{"dm":"300884","mc":"狄耐克","jys":"sz"},{"dm":"601788","mc":"光大证券","jys":"sh"},{"dm":"301348","mc":"蓝箭电子","jys":"sz"},{"dm":"605138","mc":"盛泰集团","jys":"sh"},{"dm":"688535","mc":"华海诚科","jys":"sh"},{"dm":"002748","mc":"世龙实业","jys":"sz"},{"dm":"600006","mc":"东风汽车","jys":"sh"},{"dm":"605058","mc":"澳弘电子","jys":"sh"},{"dm":"002008","mc":"大族激光","jys":"sz"},{"dm":"688296","mc":"和达科技","jys":"sh"},{"dm":"301159","mc":"三维天地","jys":"sz"},{"dm":"300560","mc":"中富通","jys":"sz"},{"dm":"603669","mc":"灵康药业","jys":"sh"},{"dm":"300426","mc":"唐德影视","jys":"sz"},{"dm":"603650","mc":"彤程新材","jys":"sh"},{"dm":"002490","mc":"山东墨龙","jys":"sz"},{"dm":"600426","mc":"华鲁恒升","jys":"sh"},{"dm":"000810","mc":"创维数字","jys":"sz"},{"dm":"600460","mc":"士兰微","jys":"sh"},{"dm":"605298","mc":"必得科技","jys":"sh"},{"dm":"600561","mc":"江西长运","jys":"sh"},{"dm":"002295","mc":"精艺股份","jys":"sz"},{"dm":"688020","mc":"方邦股份","jys":"sh"},{"dm":"603388","mc":"元成股份","jys":"sh"},{"dm":"688636","mc":"智明达","jys":"sh"},{"dm":"688588","mc":"凌志软件","jys":"sh"},{"dm":"688322","mc":"奥比中光-UW","jys":"sh"},{"dm":"688001","mc":"华兴源创","jys":"sh"},{"dm":"002491","mc":"通鼎互联","jys":"sz"},{"dm":"300964","mc":"本川智能","jys":"sz"},{"dm":"300556","mc":"丝路视觉","jys":"sz"},{"dm":"001339","mc":"智微智能","jys":"sz"},{"dm":"600256","mc":"广汇能源","jys":"sh"},{"dm":"605118","mc":"力鼎光电","jys":"sh"},{"dm":"600985","mc":"淮北矿业","jys":"sh"},{"dm":"002453","mc":"华软科技","jys":"sz"},{"dm":"300856","mc":"科思股份","jys":"sz"},{"dm":"300192","mc":"科德教育","jys":"sz"},{"dm":"688793","mc":"倍轻松","jys":"sh"},{"dm":"002331","mc":"皖通科技","jys":"sz"},{"dm":"000705","mc":"浙江震元","jys":"sz"},{"dm":"000980","mc":"众泰汽车","jys":"sz"},{"dm":"300141","mc":"和顺电气","jys":"sz"},{"dm":"000620","mc":"*ST新联","jys":"sz"},{"dm":"002137","mc":"实益达","jys":"sz"},{"dm":"000615","mc":"*ST美谷","jys":"sz"},{"dm":"603393","mc":"新天然气","jys":"sh"},{"dm":"603977","mc":"国泰集团","jys":"sh"},{"dm":"002769","mc":"普路通","jys":"sz"},{"dm":"002792","mc":"通宇通讯","jys":"sz"},{"dm":"688590","mc":"新致软件","jys":"sh"},{"dm":"002176","mc":"江特电机","jys":"sz"},{"dm":"002658","mc":"雪迪龙","jys":"sz"},{"dm":"605228","mc":"神通科技","jys":"sh"},{"dm":"600601","mc":"方正科技","jys":"sh"},{"dm":"300150","mc":"世纪瑞尔","jys":"sz"},{"dm":"301030","mc":"仕净科技","jys":"sz"},{"dm":"002134","mc":"天津普林","jys":"sz"},{"dm":"002467","mc":"二六三","jys":"sz"},{"dm":"301382","mc":"蜂助手","jys":"sz"},{"dm":"002377","mc":"国创高新","jys":"sz"},{"dm":"301252","mc":"同星科技","jys":"sz"},{"dm":"001260","mc":"坤泰股份","jys":"sz"},{"dm":"300895","mc":"铜牛信息","jys":"sz"},{"dm":"000820","mc":"神雾节能","jys":"sz"},{"dm":"603158","mc":"腾龙股份","jys":"sh"},{"dm":"300990","mc":"同飞股份","jys":"sz"},{"dm":"301357","mc":"北方长龙","jys":"sz"},{"dm":"600733","mc":"北汽蓝谷","jys":"sh"},{"dm":"688357","mc":"建龙微纳","jys":"sh"},{"dm":"300520","mc":"科大国创","jys":"sz"},{"dm":"000416","mc":"*ST民控","jys":"sz"},{"dm":"301172","mc":"君逸数码","jys":"sz"},{"dm":"002643","mc":"万润股份","jys":"sz"},{"dm":"605198","mc":"安德利","jys":"sh"},{"dm":"600983","mc":"惠而浦","jys":"sh"},{"dm":"000718","mc":"苏宁环球","jys":"sz"},{"dm":"600780","mc":"通宝能源","jys":"sh"},{"dm":"300455","mc":"航天智装","jys":"sz"},{"dm":"002190","mc":"成飞集成","jys":"sz"},{"dm":"600989","mc":"宝丰能源","jys":"sh"},{"dm":"002362","mc":"汉王科技","jys":"sz"},{"dm":"688112","mc":"鼎阳科技","jys":"sh"},{"dm":"002979","mc":"雷赛智能","jys":"sz"},{"dm":"601162","mc":"天风证券","jys":"sh"},{"dm":"002640","mc":"跨境通","jys":"sz"},{"dm":"002306","mc":"中科云网","jys":"sz"},{"dm":"603556","mc":"海兴电力","jys":"sh"},{"dm":"603297","mc":"永新光学","jys":"sh"},{"dm":"688696","mc":"极米科技","jys":"sh"},{"dm":"002442","mc":"龙星化工","jys":"sz"},{"dm":"000429","mc":"粤高速A","jys":"sz"},{"dm":"603966","mc":"法兰泰克","jys":"sh"},{"dm":"002488","mc":"金固股份","jys":"sz"},{"dm":"601236","mc":"红塔证券","jys":"sh"},{"dm":"601328","mc":"交通银行","jys":"sh"},{"dm":"300045","mc":"华力创通","jys":"sz"},{"dm":"301251","mc":"威尔高","jys":"sz"},{"dm":"688051","mc":"佳华科技","jys":"sh"},{"dm":"000039","mc":"中集集团","jys":"sz"},{"dm":"002843","mc":"泰嘉股份","jys":"sz"},{"dm":"300215","mc":"电科院","jys":"sz"},{"dm":"002410","mc":"广联达","jys":"sz"},{"dm":"002224","mc":"三 力 士","jys":"sz"},{"dm":"300802","mc":"矩子科技","jys":"sz"},{"dm":"000665","mc":"湖北广电","jys":"sz"},{"dm":"301512","mc":"智信精密","jys":"sz"},{"dm":"300221","mc":"银禧科技","jys":"sz"},{"dm":"002457","mc":"青龙管业","jys":"sz"},{"dm":"301529","mc":"福赛科技","jys":"sz"},{"dm":"301288","mc":"清研环境","jys":"sz"},{"dm":"300199","mc":"翰宇药业","jys":"sz"},{"dm":"603686","mc":"福龙马","jys":"sh"},{"dm":"605133","mc":"嵘泰股份","jys":"sh"},{"dm":"301181","mc":"标榜股份","jys":"sz"},{"dm":"300167","mc":"ST迪威迅","jys":"sz"},{"dm":"300730","mc":"科创信息","jys":"sz"},{"dm":"002454","mc":"松芝股份","jys":"sz"},{"dm":"301328","mc":"维峰电子","jys":"sz"},{"dm":"300659","mc":"中孚信息","jys":"sz"},{"dm":"600993","mc":"马应龙","jys":"sh"},{"dm":"688246","mc":"嘉和美康","jys":"sh"},{"dm":"603881","mc":"数据港","jys":"sh"},{"dm":"300614","mc":"百川畅银","jys":"sz"},{"dm":"600689","mc":"上海三毛","jys":"sh"},{"dm":"002073","mc":"软控股份","jys":"sz"},{"dm":"300344","mc":"立方数科","jys":"sz"},{"dm":"301129","mc":"瑞纳智能","jys":"sz"},{"dm":"600012","mc":"皖通高速","jys":"sh"},{"dm":"300333","mc":"兆日科技","jys":"sz"},{"dm":"603392","mc":"万泰生物","jys":"sh"},{"dm":"000638","mc":"万方发展","jys":"sz"},{"dm":"002357","mc":"富临运业","jys":"sz"},{"dm":"603086","mc":"先达股份","jys":"sh"},{"dm":"688776","mc":"国光电气","jys":"sh"},{"dm":"301372","mc":"科净源","jys":"sz"},{"dm":"301428","mc":"世纪恒通","jys":"sz"},{"dm":"300525","mc":"博思软件","jys":"sz"},{"dm":"603139","mc":"康惠制药","jys":"sh"},{"dm":"688628","mc":"优利德","jys":"sh"},{"dm":"300461","mc":"田中精机","jys":"sz"},{"dm":"300517","mc":"海波重科","jys":"sz"},{"dm":"300591","mc":"万里马","jys":"sz"},{"dm":"300241","mc":"瑞丰光电","jys":"sz"},{"dm":"688429","mc":"时创能源","jys":"sh"},{"dm":"688138","mc":"清溢光电","jys":"sh"},{"dm":"600290","mc":"*ST华仪","jys":"sh"},{"dm":"300084","mc":"海默科技","jys":"sz"},{"dm":"002632","mc":"道明光学","jys":"sz"},{"dm":"300547","mc":"川环科技","jys":"sz"},{"dm":"600615","mc":"丰华股份","jys":"sh"},{"dm":"002919","mc":"名臣健康","jys":"sz"},{"dm":"001965","mc":"招商公路","jys":"sz"},{"dm":"002268","mc":"电科网安","jys":"sz"},{"dm":"603289","mc":"泰瑞机器","jys":"sh"},{"dm":"300866","mc":"安克创新","jys":"sz"},{"dm":"300277","mc":"海联讯","jys":"sz"},{"dm":"688077","mc":"大地熊","jys":"sh"},{"dm":"603937","mc":"丽岛新材","jys":"sh"},{"dm":"003005","mc":"竞业达","jys":"sz"},{"dm":"688693","mc":"锴威特","jys":"sh"},{"dm":"600793","mc":"宜宾纸业","jys":"sh"},{"dm":"300229","mc":"拓尔思","jys":"sz"},{"dm":"688621","mc":"阳光诺和","jys":"sh"},{"dm":"001289","mc":"龙源电力","jys":"sz"},{"dm":"688076","mc":"诺泰生物","jys":"sh"},{"dm":"002777","mc":"久远银海","jys":"sz"},{"dm":"002811","mc":"郑中设计","jys":"sz"},{"dm":"601789","mc":"宁波建工","jys":"sh"},{"dm":"600841","mc":"动力新科","jys":"sh"},{"dm":"300120","mc":"经纬辉开","jys":"sz"},{"dm":"688766","mc":"普冉股份","jys":"sh"},{"dm":"002148","mc":"北纬科技","jys":"sz"},{"dm":"688099","mc":"晶晨股份","jys":"sh"},{"dm":"603059","mc":"倍加洁","jys":"sh"},{"dm":"600684","mc":"珠江股份","jys":"sh"},{"dm":"601699","mc":"潞安环能","jys":"sh"},{"dm":"688160","mc":"步科股份","jys":"sh"},{"dm":"002590","mc":"万安科技","jys":"sz"},{"dm":"603189","mc":"网达软件","jys":"sh"},{"dm":"001269","mc":"欧晶科技","jys":"sz"},{"dm":"000856","mc":"冀东装备","jys":"sz"},{"dm":"603230","mc":"内蒙新华","jys":"sh"},{"dm":"300061","mc":"旗天科技","jys":"sz"},{"dm":"301248","mc":"杰创智能","jys":"sz"},{"dm":"605318","mc":"法狮龙","jys":"sh"},{"dm":"300051","mc":"琏升科技","jys":"sz"},{"dm":"603052","mc":"可川科技","jys":"sh"},{"dm":"300723","mc":"一品红","jys":"sz"},{"dm":"603197","mc":"保隆科技","jys":"sh"},{"dm":"300445","mc":"康斯特","jys":"sz"},{"dm":"301068","mc":"大地海洋","jys":"sz"},{"dm":"300503","mc":"昊志机电","jys":"sz"},{"dm":"300334","mc":"津膜科技","jys":"sz"},{"dm":"002952","mc":"亚世光电","jys":"sz"},{"dm":"688181","mc":"八亿时空","jys":"sh"},{"dm":"301162","mc":"国能日新","jys":"sz"},{"dm":"300537","mc":"广信材料","jys":"sz"},{"dm":"002368","mc":"太极股份","jys":"sz"},{"dm":"603586","mc":"金麒麟","jys":"sh"},{"dm":"300601","mc":"康泰生物","jys":"sz"},{"dm":"603039","mc":"泛微网络","jys":"sh"},{"dm":"605020","mc":"永和股份","jys":"sh"},{"dm":"688373","mc":"盟科药业-U","jys":"sh"},{"dm":"301188","mc":"力诺特玻","jys":"sz"},{"dm":"603229","mc":"奥翔药业","jys":"sh"},{"dm":"301231","mc":"荣信文化","jys":"sz"},{"dm":"001296","mc":"长江材料","jys":"sz"},{"dm":"601198","mc":"东兴证券","jys":"sh"},{"dm":"002166","mc":"莱茵生物","jys":"sz"},{"dm":"603936","mc":"博敏电子","jys":"sh"},{"dm":"688579","mc":"山大地纬","jys":"sh"},{"dm":"300057","mc":"万顺新材","jys":"sz"},{"dm":"688045","mc":"必易微","jys":"sh"},{"dm":"301299","mc":"卓创资讯","jys":"sz"},{"dm":"300217","mc":"东方电热","jys":"sz"},{"dm":"600558","mc":"大西洋","jys":"sh"},{"dm":"300805","mc":"电声股份","jys":"sz"},{"dm":"600415","mc":"小商品城","jys":"sh"},{"dm":"000571","mc":"新大洲A","jys":"sz"},{"dm":"002094","mc":"青岛金王","jys":"sz"},{"dm":"002031","mc":"巨轮智能","jys":"sz"},{"dm":"688582","mc":"芯动联科","jys":"sh"},{"dm":"688323","mc":"瑞华泰","jys":"sh"},{"dm":"603992","mc":"松霖科技","jys":"sh"},{"dm":"688237","mc":"超卓航科","jys":"sh"},{"dm":"600129","mc":"太极集团","jys":"sh"},{"dm":"002671","mc":"龙泉股份","jys":"sz"},{"dm":"000795","mc":"英洛华","jys":"sz"},{"dm":"688191","mc":"智洋创新","jys":"sh"},{"dm":"600355","mc":"精伦电子","jys":"sh"},{"dm":"002263","mc":"大东南","jys":"sz"},{"dm":"688573","mc":"信宇人","jys":"sh"},{"dm":"300877","mc":"金春股份","jys":"sz"},{"dm":"600053","mc":"九鼎投资","jys":"sh"},{"dm":"601500","mc":"通用股份","jys":"sh"},{"dm":"001282","mc":"三联锻造","jys":"sz"},{"dm":"603501","mc":"韦尔股份","jys":"sh"},{"dm":"300949","mc":"奥雅股份","jys":"sz"},{"dm":"000971","mc":"ST高升","jys":"sz"},{"dm":"600769","mc":"祥龙电业","jys":"sh"},{"dm":"600032","mc":"浙江新能","jys":"sh"},{"dm":"600039","mc":"四川路桥","jys":"sh"},{"dm":"603319","mc":"湘油泵","jys":"sh"},{"dm":"600345","mc":"长江通信","jys":"sh"},{"dm":"603530","mc":"神马电力","jys":"sh"},{"dm":"301302","mc":"华如科技","jys":"sz"},{"dm":"603928","mc":"兴业股份","jys":"sh"},{"dm":"001206","mc":"依依股份","jys":"sz"},{"dm":"002862","mc":"实丰文化","jys":"sz"},{"dm":"600735","mc":"新华锦","jys":"sh"},{"dm":"300916","mc":"朗特智能","jys":"sz"},{"dm":"002752","mc":"昇兴股份","jys":"sz"},{"dm":"603327","mc":"福蓉科技","jys":"sh"},{"dm":"603188","mc":"亚邦股份","jys":"sh"},{"dm":"605199","mc":"葫芦娃","jys":"sh"},{"dm":"600758","mc":"辽宁能源","jys":"sh"},{"dm":"601969","mc":"海南矿业","jys":"sh"},{"dm":"002521","mc":"齐峰新材","jys":"sz"},{"dm":"688147","mc":"微导纳米","jys":"sh"},{"dm":"301266","mc":"宇邦新材","jys":"sz"},{"dm":"300474","mc":"景嘉微","jys":"sz"},{"dm":"002215","mc":"诺 普 信","jys":"sz"},{"dm":"603119","mc":"浙江荣泰","jys":"sh"},{"dm":"002591","mc":"恒大高新","jys":"sz"},{"dm":"300703","mc":"创源股份","jys":"sz"},{"dm":"603363","mc":"傲农生物","jys":"sh"},{"dm":"300365","mc":"恒华科技","jys":"sz"},{"dm":"301267","mc":"华厦眼科","jys":"sz"},{"dm":"002395","mc":"双象股份","jys":"sz"},{"dm":"688329","mc":"艾隆科技","jys":"sh"},{"dm":"605088","mc":"冠盛股份","jys":"sh"},{"dm":"600604","mc":"市北高新","jys":"sh"},{"dm":"000968","mc":"蓝焰控股","jys":"sz"},{"dm":"600375","mc":"汉马科技","jys":"sh"},{"dm":"300935","mc":"盈建科","jys":"sz"},{"dm":"601918","mc":"新集能源","jys":"sh"},{"dm":"603662","mc":"柯力传感","jys":"sh"},{"dm":"301366","mc":"一博科技","jys":"sz"},{"dm":"600864","mc":"哈投股份","jys":"sh"},{"dm":"002400","mc":"省广集团","jys":"sz"},{"dm":"600125","mc":"铁龙物流","jys":"sh"},{"dm":"002576","mc":"通达动力","jys":"sz"},{"dm":"001298","mc":"好上好","jys":"sz"},{"dm":"300414","mc":"中光防雷","jys":"sz"},{"dm":"002315","mc":"焦点科技","jys":"sz"},{"dm":"002109","mc":"兴化股份","jys":"sz"},{"dm":"688004","mc":"博汇科技","jys":"sh"},{"dm":"300314","mc":"戴维医疗","jys":"sz"},{"dm":"301203","mc":"国泰环保","jys":"sz"},{"dm":"002593","mc":"日上集团","jys":"sz"},{"dm":"300248","mc":"新开普","jys":"sz"},{"dm":"601999","mc":"出版传媒","jys":"sh"},{"dm":"002787","mc":"华源控股","jys":"sz"},{"dm":"000525","mc":"ST红太阳","jys":"sz"},{"dm":"300936","mc":"中英科技","jys":"sz"},{"dm":"300497","mc":"富祥药业","jys":"sz"},{"dm":"603618","mc":"杭电股份","jys":"sh"},{"dm":"002189","mc":"中光学","jys":"sz"},{"dm":"300368","mc":"汇金股份","jys":"sz"},{"dm":"300227","mc":"光韵达","jys":"sz"},{"dm":"000823","mc":"超声电子","jys":"sz"},{"dm":"600330","mc":"天通股份","jys":"sh"},{"dm":"003040","mc":"楚天龙","jys":"sz"},{"dm":"001226","mc":"拓山重工","jys":"sz"},{"dm":"300260","mc":"新莱应材","jys":"sz"},{"dm":"300237","mc":"美晨生态","jys":"sz"},{"dm":"600773","mc":"西藏城投","jys":"sh"},{"dm":"603138","mc":"海量数据","jys":"sh"},{"dm":"002015","mc":"协鑫能科","jys":"sz"},{"dm":"000727","mc":"冠捷科技","jys":"sz"},{"dm":"000920","mc":"沃顿科技","jys":"sz"},{"dm":"600671","mc":"*ST目药","jys":"sh"},{"dm":"002585","mc":"双星新材","jys":"sz"},{"dm":"300403","mc":"汉宇集团","jys":"sz"},{"dm":"002087","mc":"*ST新纺","jys":"sz"},{"dm":"688435","mc":"英方软件","jys":"sh"},{"dm":"300355","mc":"蒙草生态","jys":"sz"},{"dm":"600738","mc":"丽尚国潮","jys":"sh"},{"dm":"600641","mc":"万业企业","jys":"sh"},{"dm":"002230","mc":"科大讯飞","jys":"sz"},{"dm":"300604","mc":"长川科技","jys":"sz"},{"dm":"300638","mc":"广和通","jys":"sz"},{"dm":"301528","mc":"多浦乐","jys":"sz"},{"dm":"002512","mc":"达华智能","jys":"sz"},{"dm":"300546","mc":"雄帝科技","jys":"sz"},{"dm":"600329","mc":"达仁堂","jys":"sh"},{"dm":"603379","mc":"三美股份","jys":"sh"},{"dm":"600241","mc":"时代万恒","jys":"sh"},{"dm":"300281","mc":"金明精机","jys":"sz"},{"dm":"603010","mc":"万盛股份","jys":"sh"},{"dm":"600088","mc":"中视传媒","jys":"sh"},{"dm":"688372","mc":"伟测科技","jys":"sh"},{"dm":"301019","mc":"宁波色母","jys":"sz"},{"dm":"600567","mc":"山鹰国际","jys":"sh"},{"dm":"600281","mc":"华阳新材","jys":"sh"},{"dm":"300693","mc":"盛弘股份","jys":"sz"},{"dm":"000953","mc":"河化股份","jys":"sz"},{"dm":"002825","mc":"纳尔股份","jys":"sz"},{"dm":"002757","mc":"南兴股份","jys":"sz"},{"dm":"301380","mc":"挖金客","jys":"sz"},{"dm":"002336","mc":"人人乐","jys":"sz"},{"dm":"603926","mc":"铁流股份","jys":"sh"},{"dm":"300504","mc":"天邑股份","jys":"sz"},{"dm":"300079","mc":"数码视讯","jys":"sz"},{"dm":"600626","mc":"申达股份","jys":"sh"},{"dm":"300322","mc":"硕贝德","jys":"sz"},{"dm":"688619","mc":"罗普特","jys":"sh"},{"dm":"301007","mc":"德迈仕","jys":"sz"},{"dm":"300130","mc":"新国都","jys":"sz"},{"dm":"300250","mc":"初灵信息","jys":"sz"},{"dm":"000407","mc":"胜利股份","jys":"sz"},{"dm":"603823","mc":"百合花","jys":"sh"},{"dm":"002661","mc":"克明食品","jys":"sz"},{"dm":"000554","mc":"泰山石油","jys":"sz"},{"dm":"002234","mc":"民和股份","jys":"sz"},{"dm":"002662","mc":"京威股份","jys":"sz"},{"dm":"002642","mc":"荣联科技","jys":"sz"},{"dm":"002212","mc":"天融信","jys":"sz"},{"dm":"000599","mc":"青岛双星","jys":"sz"},{"dm":"600783","mc":"鲁信创投","jys":"sh"},{"dm":"688012","mc":"中微公司","jys":"sh"},{"dm":"002857","mc":"三晖电气","jys":"sz"},{"dm":"300745","mc":"欣锐科技","jys":"sz"},{"dm":"600199","mc":"金种子酒","jys":"sh"},{"dm":"600734","mc":"ST实达","jys":"sh"},{"dm":"300543","mc":"朗科智能","jys":"sz"},{"dm":"002418","mc":"康盛股份","jys":"sz"},{"dm":"301016","mc":"雷尔伟","jys":"sz"},{"dm":"300561","mc":"汇金科技","jys":"sz"},{"dm":"600523","mc":"贵航股份","jys":"sh"},{"dm":"605166","mc":"聚合顺","jys":"sh"},{"dm":"601858","mc":"中国科传","jys":"sh"},{"dm":"603948","mc":"建业股份","jys":"sh"},{"dm":"688600","mc":"皖仪科技","jys":"sh"},{"dm":"301261","mc":"恒工精密","jys":"sz"},{"dm":"301062","mc":"上海艾录","jys":"sz"},{"dm":"000421","mc":"南京公用","jys":"sz"},{"dm":"301208","mc":"中亦科技","jys":"sz"},{"dm":"002761","mc":"浙江建投","jys":"sz"},{"dm":"002316","mc":"亚联发展","jys":"sz"},{"dm":"600288","mc":"大恒科技","jys":"sh"},{"dm":"300451","mc":"创业慧康","jys":"sz"},{"dm":"603109","mc":"神驰机电","jys":"sh"},{"dm":"603040","mc":"新坐标","jys":"sh"},{"dm":"001259","mc":"利仁科技","jys":"sz"},{"dm":"688667","mc":"菱电电控","jys":"sh"},{"dm":"600658","mc":"电子城","jys":"sh"},{"dm":"603172","mc":"万丰股份","jys":"sh"},{"dm":"300004","mc":"南风股份","jys":"sz"},{"dm":"301315","mc":"威士顿","jys":"sz"},{"dm":"603486","mc":"科沃斯","jys":"sh"},{"dm":"688448","mc":"磁谷科技","jys":"sh"},{"dm":"300513","mc":"恒实科技","jys":"sz"},{"dm":"600599","mc":"ST熊猫","jys":"sh"},{"dm":"000070","mc":"特发信息","jys":"sz"},{"dm":"601988","mc":"中国银行","jys":"sh"},{"dm":"600128","mc":"苏豪弘业","jys":"sh"},{"dm":"600007","mc":"中国国贸","jys":"sh"},{"dm":"300319","mc":"麦捷科技","jys":"sz"},{"dm":"603813","mc":"原尚股份","jys":"sh"},{"dm":"300762","mc":"上海瀚讯","jys":"sz"},{"dm":"002679","mc":"福建金森","jys":"sz"},{"dm":"002310","mc":"东方园林","jys":"sz"},{"dm":"688568","mc":"中科星图","jys":"sh"},{"dm":"002715","mc":"登云股份","jys":"sz"},{"dm":"603788","mc":"宁波高发","jys":"sh"},{"dm":"301399","mc":"英特科技","jys":"sz"},{"dm":"300471","mc":"厚普股份","jys":"sz"},{"dm":"688480","mc":"赛恩斯","jys":"sh"},{"dm":"002019","mc":"亿帆医药","jys":"sz"},{"dm":"600229","mc":"城市传媒","jys":"sh"},{"dm":"600885","mc":"宏发股份","jys":"sh"},{"dm":"002835","mc":"同为股份","jys":"sz"},{"dm":"688209","mc":"英集芯","jys":"sh"},{"dm":"600796","mc":"钱江生化","jys":"sh"},{"dm":"000025","mc":"特 力A","jys":"sz"},{"dm":"605333","mc":"沪光股份","jys":"sh"},{"dm":"002840","mc":"华统股份","jys":"sz"},{"dm":"001268","mc":"联合精密","jys":"sz"},{"dm":"301113","mc":"雅艺科技","jys":"sz"},{"dm":"300296","mc":"利亚德","jys":"sz"},{"dm":"688228","mc":"开普云","jys":"sh"},{"dm":"300689","mc":"澄天伟业","jys":"sz"},{"dm":"300041","mc":"回天新材","jys":"sz"},{"dm":"300958","mc":"建工修复","jys":"sz"},{"dm":"300880","mc":"迦南智能","jys":"sz"},{"dm":"600546","mc":"山煤国际","jys":"sh"},{"dm":"300498","mc":"温氏股份","jys":"sz"},{"dm":"600118","mc":"中国卫星","jys":"sh"},{"dm":"002875","mc":"安奈儿","jys":"sz"},{"dm":"002698","mc":"博实股份","jys":"sz"},{"dm":"301331","mc":"恩威医药","jys":"sz"},{"dm":"688103","mc":"国力股份","jys":"sh"},{"dm":"603360","mc":"百傲化学","jys":"sh"},{"dm":"002775","mc":"文科园林","jys":"sz"},{"dm":"688290","mc":"景业智能","jys":"sh"},{"dm":"000811","mc":"冰轮环境","jys":"sz"},{"dm":"301225","mc":"恒勃股份","jys":"sz"},{"dm":"301157","mc":"华塑科技","jys":"sz"},{"dm":"603776","mc":"永安行","jys":"sh"},{"dm":"688800","mc":"瑞可达","jys":"sh"},{"dm":"300761","mc":"立华股份","jys":"sz"},{"dm":"300173","mc":"福能东方","jys":"sz"},{"dm":"300153","mc":"科泰电源","jys":"sz"},{"dm":"688522","mc":"纳睿雷达","jys":"sh"},{"dm":"300213","mc":"佳讯飞鸿","jys":"sz"},{"dm":"301525","mc":"儒竞科技","jys":"sz"},{"dm":"300377","mc":"赢时胜","jys":"sz"},{"dm":"603660","mc":"苏州科达","jys":"sh"},{"dm":"300494","mc":"盛天网络","jys":"sz"},{"dm":"688618","mc":"三旺通信","jys":"sh"},{"dm":"002083","mc":"孚日股份","jys":"sz"},{"dm":"000998","mc":"隆平高科","jys":"sz"},{"dm":"003010","mc":"若羽臣","jys":"sz"},{"dm":"600938","mc":"中国海油","jys":"sh"},{"dm":"600584","mc":"长电科技","jys":"sh"},{"dm":"300634","mc":"彩讯股份","jys":"sz"},{"dm":"300598","mc":"诚迈科技","jys":"sz"},{"dm":"301083","mc":"百胜智能","jys":"sz"},{"dm":"002803","mc":"吉宏股份","jys":"sz"},{"dm":"603123","mc":"翠微股份","jys":"sh"},{"dm":"300720","mc":"海川智能","jys":"sz"},{"dm":"688571","mc":"杭华股份","jys":"sh"},{"dm":"000678","mc":"襄阳轴承","jys":"sz"},{"dm":"600792","mc":"云煤能源","jys":"sh"},{"dm":"688169","mc":"石头科技","jys":"sh"},{"dm":"600536","mc":"中国软件","jys":"sh"},{"dm":"300288","mc":"朗玛信息","jys":"sz"},{"dm":"600059","mc":"古越龙山","jys":"sh"},{"dm":"600232","mc":"金鹰股份","jys":"sh"},{"dm":"603959","mc":"百利科技","jys":"sh"},{"dm":"002628","mc":"成都路桥","jys":"sz"},{"dm":"300085","mc":"银之杰","jys":"sz"},{"dm":"688767","mc":"博拓生物","jys":"sh"},{"dm":"300621","mc":"维业股份","jys":"sz"},{"dm":"600250","mc":"南纺股份","jys":"sh"},{"dm":"000826","mc":"启迪环境","jys":"sz"},{"dm":"300399","mc":"天利科技","jys":"sz"},{"dm":"000757","mc":"浩物股份","jys":"sz"},{"dm":"002276","mc":"万马股份","jys":"sz"},{"dm":"688201","mc":"信安世纪","jys":"sh"},{"dm":"600909","mc":"华安证券","jys":"sh"},{"dm":"300928","mc":"华安鑫创","jys":"sz"},{"dm":"688282","mc":"理工导航","jys":"sh"},{"dm":"002808","mc":"ST恒久","jys":"sz"},{"dm":"601225","mc":"陕西煤业","jys":"sh"},{"dm":"688610","mc":"埃科光电","jys":"sh"},{"dm":"002335","mc":"科华数据","jys":"sz"},{"dm":"688626","mc":"翔宇医疗","jys":"sh"},{"dm":"688591","mc":"泰凌微","jys":"sh"},{"dm":"688021","mc":"奥福环保","jys":"sh"},{"dm":"300113","mc":"顺网科技","jys":"sz"},{"dm":"002763","mc":"汇洁股份","jys":"sz"},{"dm":"300493","mc":"润欣科技","jys":"sz"},{"dm":"001331","mc":"胜通能源","jys":"sz"},{"dm":"603278","mc":"大业股份","jys":"sh"},{"dm":"300410","mc":"正业科技","jys":"sz"},{"dm":"002630","mc":"华西能源","jys":"sz"},{"dm":"603908","mc":"牧高笛","jys":"sh"},{"dm":"300226","mc":"上海钢联","jys":"sz"},{"dm":"688113","mc":"联测科技","jys":"sh"},{"dm":"300948","mc":"冠中生态","jys":"sz"},{"dm":"688068","mc":"热景生物","jys":"sh"},{"dm":"600369","mc":"西南证券","jys":"sh"},{"dm":"002673","mc":"西部证券","jys":"sz"},{"dm":"300454","mc":"深信服","jys":"sz"},{"dm":"002577","mc":"雷柏科技","jys":"sz"},{"dm":"300112","mc":"万讯自控","jys":"sz"},{"dm":"603206","mc":"嘉环科技","jys":"sh"},{"dm":"600025","mc":"华能水电","jys":"sh"},{"dm":"300219","mc":"鸿利智汇","jys":"sz"},{"dm":"002235","mc":"安妮股份","jys":"sz"},{"dm":"002446","mc":"盛路通信","jys":"sz"},{"dm":"605180","mc":"华生科技","jys":"sh"},{"dm":"688439","mc":"振华风光","jys":"sh"},{"dm":"300577","mc":"开润股份","jys":"sz"},{"dm":"002893","mc":"京能热力","jys":"sz"},{"dm":"300323","mc":"华灿光电","jys":"sz"},{"dm":"300559","mc":"佳发教育","jys":"sz"},{"dm":"002360","mc":"同德化工","jys":"sz"},{"dm":"600423","mc":"柳化股份","jys":"sh"},{"dm":"002351","mc":"漫步者","jys":"sz"},{"dm":"605016","mc":"百龙创园","jys":"sh"},{"dm":"300627","mc":"华测导航","jys":"sz"},{"dm":"688539","mc":"高华科技","jys":"sh"},{"dm":"600076","mc":"康欣新材","jys":"sh"},{"dm":"300830","mc":"金现代","jys":"sz"},{"dm":"300342","mc":"天银机电","jys":"sz"},{"dm":"000631","mc":"顺发恒业","jys":"sz"},{"dm":"688580","mc":"伟思医疗","jys":"sh"},{"dm":"600686","mc":"金龙汽车","jys":"sh"},{"dm":"600061","mc":"国投资本","jys":"sh"},{"dm":"688360","mc":"德马科技","jys":"sh"},{"dm":"002518","mc":"科士达","jys":"sz"},{"dm":"603176","mc":"汇通集团","jys":"sh"},{"dm":"000782","mc":"美达股份","jys":"sz"},{"dm":"605066","mc":"天正电气","jys":"sh"},{"dm":"002023","mc":"海特高新","jys":"sz"},{"dm":"002133","mc":"广宇集团","jys":"sz"},{"dm":"300077","mc":"国民技术","jys":"sz"},{"dm":"002957","mc":"科瑞技术","jys":"sz"},{"dm":"002348","mc":"高乐股份","jys":"sz"},{"dm":"600707","mc":"彩虹股份","jys":"sh"},{"dm":"002396","mc":"星网锐捷","jys":"sz"},{"dm":"300713","mc":"英可瑞","jys":"sz"},{"dm":"000762","mc":"西藏矿业","jys":"sz"},{"dm":"300338","mc":"开元教育","jys":"sz"},{"dm":"000962","mc":"东方钽业","jys":"sz"},{"dm":"300937","mc":"药易购","jys":"sz"},{"dm":"688365","mc":"光云科技","jys":"sh"},{"dm":"300961","mc":"深水海纳","jys":"sz"},{"dm":"300115","mc":"长盈精密","jys":"sz"},{"dm":"601869","mc":"长飞光纤","jys":"sh"},{"dm":"600971","mc":"恒源煤电","jys":"sh"},{"dm":"688219","mc":"会通股份","jys":"sh"},{"dm":"002749","mc":"国光股份","jys":"sz"},{"dm":"000166","mc":"申万宏源","jys":"sz"},{"dm":"300440","mc":"运达科技","jys":"sz"},{"dm":"300476","mc":"胜宏科技","jys":"sz"},{"dm":"000911","mc":"广农糖业","jys":"sz"},{"dm":"002058","mc":"威尔泰","jys":"sz"},{"dm":"300605","mc":"恒锋信息","jys":"sz"},{"dm":"002207","mc":"准油股份","jys":"sz"},{"dm":"600830","mc":"香溢融通","jys":"sh"},{"dm":"002354","mc":"天娱数科","jys":"sz"},{"dm":"002722","mc":"物产金轮","jys":"sz"},{"dm":"688123","mc":"聚辰股份","jys":"sh"},{"dm":"603825","mc":"华扬联众","jys":"sh"},{"dm":"000679","mc":"大连友谊","jys":"sz"},{"dm":"600997","mc":"开滦股份","jys":"sh"},{"dm":"600336","mc":"澳柯玛","jys":"sh"},{"dm":"688620","mc":"安凯微","jys":"sh"},{"dm":"605388","mc":"均瑶健康","jys":"sh"},{"dm":"600185","mc":"格力地产","jys":"sh"},{"dm":"002766","mc":"索菱股份","jys":"sz"},{"dm":"000011","mc":"深物业A","jys":"sz"},{"dm":"601377","mc":"兴业证券","jys":"sh"},{"dm":"002480","mc":"新筑股份","jys":"sz"},{"dm":"688090","mc":"瑞松科技","jys":"sh"},{"dm":"600683","mc":"京投发展","jys":"sh"},{"dm":"603728","mc":"鸣志电器","jys":"sh"},{"dm":"002649","mc":"博彦科技","jys":"sz"},{"dm":"002828","mc":"贝肯能源","jys":"sz"},{"dm":"301153","mc":"中科江南","jys":"sz"},{"dm":"603927","mc":"中科软","jys":"sh"},{"dm":"301379","mc":"天山电子","jys":"sz"},{"dm":"300557","mc":"理工光科","jys":"sz"},{"dm":"301105","mc":"鸿铭股份","jys":"sz"},{"dm":"000821","mc":"京山轻机","jys":"sz"},{"dm":"603980","mc":"吉华集团","jys":"sh"},{"dm":"300353","mc":"东土科技","jys":"sz"},{"dm":"301311","mc":"昆船智能","jys":"sz"},{"dm":"300858","mc":"科拓生物","jys":"sz"},{"dm":"688309","mc":"恒誉环保","jys":"sh"},{"dm":"603329","mc":"上海雅仕","jys":"sh"},{"dm":"002479","mc":"富春环保","jys":"sz"},{"dm":"688022","mc":"瀚川智能","jys":"sh"},{"dm":"300201","mc":"海伦哲","jys":"sz"},{"dm":"688612","mc":"威迈斯","jys":"sh"},{"dm":"002406","mc":"远东传动","jys":"sz"},{"dm":"603273","mc":"天元智能","jys":"sh"},{"dm":"301369","mc":"联动科技","jys":"sz"},{"dm":"000766","mc":"通化金马","jys":"sz"},{"dm":"600227","mc":"赤天化","jys":"sh"},{"dm":"600016","mc":"民生银行","jys":"sh"},{"dm":"300722","mc":"新余国科","jys":"sz"},{"dm":"301032","mc":"新柴股份","jys":"sz"},{"dm":"603607","mc":"京华激光","jys":"sh"},{"dm":"688786","mc":"悦安新材","jys":"sh"},{"dm":"002975","mc":"博杰股份","jys":"sz"},{"dm":"601929","mc":"吉视传媒","jys":"sh"},{"dm":"300058","mc":"蓝色光标","jys":"sz"},{"dm":"002647","mc":"仁东控股","jys":"sz"},{"dm":"688376","mc":"美埃科技","jys":"sh"},{"dm":"300162","mc":"雷曼光电","jys":"sz"},{"dm":"002953","mc":"日丰股份","jys":"sz"},{"dm":"600722","mc":"金牛化工","jys":"sh"},{"dm":"300425","mc":"中建环能","jys":"sz"},{"dm":"000868","mc":"安凯客车","jys":"sz"},{"dm":"603511","mc":"爱慕股份","jys":"sh"},{"dm":"600549","mc":"厦门钨业","jys":"sh"},{"dm":"600202","mc":"哈空调","jys":"sh"},{"dm":"000973","mc":"佛塑科技","jys":"sz"},{"dm":"603880","mc":"ST南卫","jys":"sh"},{"dm":"603500","mc":"祥和实业","jys":"sh"},{"dm":"002465","mc":"海格通信","jys":"sz"},{"dm":"300127","mc":"银河磁体","jys":"sz"},{"dm":"600653","mc":"申华控股","jys":"sh"},{"dm":"300969","mc":"恒帅股份","jys":"sz"},{"dm":"600751","mc":"海航科技","jys":"sh"},{"dm":"301339","mc":"通行宝","jys":"sz"},{"dm":"002547","mc":"春兴精工","jys":"sz"},{"dm":"300466","mc":"赛摩智能","jys":"sz"},{"dm":"002117","mc":"东港股份","jys":"sz"},{"dm":"002820","mc":"桂发祥","jys":"sz"},{"dm":"603221","mc":"爱丽家居","jys":"sh"},{"dm":"603800","mc":"道森股份","jys":"sh"},{"dm":"603610","mc":"麒盛科技","jys":"sh"},{"dm":"000909","mc":"ST数源","jys":"sz"},{"dm":"688075","mc":"安旭生物","jys":"sh"},{"dm":"300987","mc":"川网传媒","jys":"sz"},{"dm":"603178","mc":"圣龙股份","jys":"sh"},{"dm":"301020","mc":"密封科技","jys":"sz"},{"dm":"002899","mc":"英派斯","jys":"sz"},{"dm":"002884","mc":"凌霄泵业","jys":"sz"},{"dm":"300228","mc":"富瑞特装","jys":"sz"},{"dm":"300380","mc":"安硕信息","jys":"sz"},{"dm":"002211","mc":"宏达新材","jys":"sz"},{"dm":"002856","mc":"美芝股份","jys":"sz"},{"dm":"002124","mc":"天邦食品","jys":"sz"},{"dm":"002063","mc":"远光软件","jys":"sz"},{"dm":"300515","mc":"三德科技","jys":"sz"},{"dm":"300509","mc":"新美星","jys":"sz"},{"dm":"002997","mc":"瑞鹄模具","jys":"sz"},{"dm":"300840","mc":"酷特智能","jys":"sz"},{"dm":"000040","mc":"东旭蓝天","jys":"sz"},{"dm":"600161","mc":"天坛生物","jys":"sh"},{"dm":"688225","mc":"亚信安全","jys":"sh"},{"dm":"600352","mc":"浙江龙盛","jys":"sh"},{"dm":"301272","mc":"英华特","jys":"sz"},{"dm":"300872","mc":"天阳科技","jys":"sz"},{"dm":"300625","mc":"三雄极光","jys":"sz"},{"dm":"300516","mc":"久之洋","jys":"sz"},{"dm":"688248","mc":"南网科技","jys":"sh"},{"dm":"688661","mc":"和林微纳","jys":"sh"},{"dm":"603681","mc":"永冠新材","jys":"sh"},{"dm":"300478","mc":"杭州高新","jys":"sz"},{"dm":"002184","mc":"海得控制","jys":"sz"},{"dm":"600206","mc":"有研新材","jys":"sh"},{"dm":"300386","mc":"飞天诚信","jys":"sz"},{"dm":"300885","mc":"海昌新材","jys":"sz"},{"dm":"300306","mc":"远方信息","jys":"sz"},{"dm":"300069","mc":"金利华电","jys":"sz"},{"dm":"000521","mc":"长虹美菱","jys":"sz"},{"dm":"603859","mc":"能科科技","jys":"sh"},{"dm":"002298","mc":"中电兴发","jys":"sz"},{"dm":"300991","mc":"创益通","jys":"sz"},{"dm":"301080","mc":"百普赛斯","jys":"sz"},{"dm":"002915","mc":"中欣氟材","jys":"sz"},{"dm":"000960","mc":"锡业股份","jys":"sz"},{"dm":"002852","mc":"道道全","jys":"sz"},{"dm":"300200","mc":"高盟新材","jys":"sz"},{"dm":"600051","mc":"宁波联合","jys":"sh"},{"dm":"605099","mc":"共创草坪","jys":"sh"},{"dm":"002274","mc":"华昌化工","jys":"sz"},{"dm":"300464","mc":"星徽股份","jys":"sz"},{"dm":"000752","mc":"*ST西发","jys":"sz"},{"dm":"688378","mc":"奥来德","jys":"sh"},{"dm":"002772","mc":"众兴菌业","jys":"sz"},{"dm":"300050","mc":"世纪鼎利","jys":"sz"},{"dm":"603166","mc":"福达股份","jys":"sh"},{"dm":"600365","mc":"ST通葡","jys":"sh"},{"dm":"300283","mc":"温州宏丰","jys":"sz"},{"dm":"600877","mc":"电科芯片","jys":"sh"},{"dm":"002196","mc":"方正电机","jys":"sz"},{"dm":"000576","mc":"甘化科工","jys":"sz"},{"dm":"301307","mc":"美利信","jys":"sz"},{"dm":"003001","mc":"中岩大地","jys":"sz"},{"dm":"600566","mc":"济川药业","jys":"sh"},{"dm":"603848","mc":"好太太","jys":"sh"},{"dm":"600083","mc":"博信股份","jys":"sh"},{"dm":"600319","mc":"亚星化学","jys":"sh"},{"dm":"002815","mc":"崇达技术","jys":"sz"},{"dm":"300035","mc":"中科电气","jys":"sz"},{"dm":"688701","mc":"卓锦股份","jys":"sh"},{"dm":"600366","mc":"宁波韵升","jys":"sh"},{"dm":"300578","mc":"会畅通讯","jys":"sz"},{"dm":"601066","mc":"中信建投","jys":"sh"},{"dm":"300111","mc":"向日葵","jys":"sz"},{"dm":"301237","mc":"和顺科技","jys":"sz"},{"dm":"001215","mc":"千味央厨","jys":"sz"},{"dm":"301259","mc":"艾布鲁","jys":"sz"},{"dm":"600461","mc":"洪城环境","jys":"sh"},{"dm":"301072","mc":"中捷精工","jys":"sz"},{"dm":"300075","mc":"数字政通","jys":"sz"},{"dm":"002528","mc":"英飞拓","jys":"sz"},{"dm":"000420","mc":"吉林化纤","jys":"sz"},{"dm":"002350","mc":"北京科锐","jys":"sz"},{"dm":"600448","mc":"华纺股份","jys":"sh"},{"dm":"002373","mc":"千方科技","jys":"sz"},{"dm":"300369","mc":"绿盟科技","jys":"sz"},{"dm":"301337","mc":"亚华电子","jys":"sz"},{"dm":"300590","mc":"移为通信","jys":"sz"},{"dm":"600548","mc":"深高速","jys":"sh"},{"dm":"000969","mc":"安泰科技","jys":"sz"},{"dm":"688262","mc":"国芯科技","jys":"sh"},{"dm":"000970","mc":"中科三环","jys":"sz"},{"dm":"002206","mc":"海 利 得","jys":"sz"},{"dm":"001202","mc":"炬申股份","jys":"sz"},{"dm":"002489","mc":"浙江永强","jys":"sz"},{"dm":"300785","mc":"值得买","jys":"sz"},{"dm":"688306","mc":"均普智能","jys":"sh"},{"dm":"002168","mc":"惠程科技","jys":"sz"},{"dm":"688073","mc":"毕得医药","jys":"sh"},{"dm":"002017","mc":"东信和平","jys":"sz"},{"dm":"601828","mc":"美凯龙","jys":"sh"},{"dm":"002174","mc":"游族网络","jys":"sz"},{"dm":"300716","mc":"泉为科技","jys":"sz"},{"dm":"301327","mc":"华宝新能","jys":"sz"},{"dm":"300170","mc":"汉得信息","jys":"sz"},{"dm":"300110","mc":"华仁药业","jys":"sz"},{"dm":"002140","mc":"东华科技","jys":"sz"},{"dm":"600768","mc":"宁波富邦","jys":"sh"},{"dm":"600081","mc":"东风科技","jys":"sh"},{"dm":"002879","mc":"长缆科技","jys":"sz"},{"dm":"002652","mc":"扬子新材","jys":"sz"},{"dm":"000798","mc":"中水渔业","jys":"sz"},{"dm":"603679","mc":"华体科技","jys":"sh"},{"dm":"603161","mc":"科华控股","jys":"sh"},{"dm":"001205","mc":"盛航股份","jys":"sz"},{"dm":"600302","mc":"标准股份","jys":"sh"},{"dm":"603505","mc":"金石资源","jys":"sh"},{"dm":"002011","mc":"盾安环境","jys":"sz"},{"dm":"003030","mc":"祖名股份","jys":"sz"},{"dm":"000551","mc":"创元科技","jys":"sz"},{"dm":"002796","mc":"世嘉科技","jys":"sz"},{"dm":"603311","mc":"金海高科","jys":"sh"},{"dm":"000682","mc":"东方电子","jys":"sz"},{"dm":"000572","mc":"海马汽车","jys":"sz"},{"dm":"601698","mc":"中国卫通","jys":"sh"},{"dm":"688272","mc":"*ST富吉","jys":"sh"},{"dm":"603121","mc":"华培动力","jys":"sh"},{"dm":"300108","mc":"ST吉药","jys":"sz"},{"dm":"300920","mc":"润阳科技","jys":"sz"},{"dm":"300432","mc":"富临精工","jys":"sz"},{"dm":"600236","mc":"桂冠电力","jys":"sh"},{"dm":"000949","mc":"新乡化纤","jys":"sz"},{"dm":"300565","mc":"科信技术","jys":"sz"},{"dm":"300042","mc":"朗科科技","jys":"sz"},{"dm":"000004","mc":"国华网安","jys":"sz"},{"dm":"603950","mc":"长源东谷","jys":"sh"},{"dm":"600810","mc":"神马股份","jys":"sh"},{"dm":"002050","mc":"三花智控","jys":"sz"},{"dm":"600381","mc":"青海春天","jys":"sh"},{"dm":"605056","mc":"咸亨国际","jys":"sh"},{"dm":"688008","mc":"澜起科技","jys":"sh"},{"dm":"300268","mc":"*ST佳沃","jys":"sz"},{"dm":"688215","mc":"瑞晟智能","jys":"sh"},{"dm":"603879","mc":"永悦科技","jys":"sh"},{"dm":"300809","mc":"华辰装备","jys":"sz"},{"dm":"000014","mc":"沙河股份","jys":"sz"},{"dm":"002098","mc":"浔兴股份","jys":"sz"},{"dm":"688125","mc":"安达智能","jys":"sh"},{"dm":"300189","mc":"神农科技","jys":"sz"},{"dm":"300632","mc":"光莆股份","jys":"sz"},{"dm":"001210","mc":"金房能源","jys":"sz"},{"dm":"301499","mc":"维科精密","jys":"sz"},{"dm":"002909","mc":"集泰股份","jys":"sz"},{"dm":"600730","mc":"中国高科","jys":"sh"},{"dm":"600468","mc":"百利电气","jys":"sh"},{"dm":"605500","mc":"森林包装","jys":"sh"},{"dm":"603703","mc":"盛洋科技","jys":"sh"},{"dm":"000622","mc":"恒立实业","jys":"sz"},{"dm":"603757","mc":"大元泵业","jys":"sh"},{"dm":"603958","mc":"哈森股份","jys":"sh"},{"dm":"002288","mc":"超华科技","jys":"sz"},{"dm":"002286","mc":"保龄宝","jys":"sz"},{"dm":"300903","mc":"科翔股份","jys":"sz"},{"dm":"605028","mc":"世茂能源","jys":"sh"},{"dm":"300582","mc":"英飞特","jys":"sz"},{"dm":"688403","mc":"汇成股份","jys":"sh"},{"dm":"001317","mc":"三羊马","jys":"sz"},{"dm":"601000","mc":"唐山港","jys":"sh"},{"dm":"603569","mc":"长久物流","jys":"sh"},{"dm":"688080","mc":"映翰通","jys":"sh"},{"dm":"300644","mc":"南京聚隆","jys":"sz"},{"dm":"300036","mc":"超图软件","jys":"sz"},{"dm":"603320","mc":"迪贝电气","jys":"sh"},{"dm":"002159","mc":"三特索道","jys":"sz"},{"dm":"600831","mc":"广电网络","jys":"sh"},{"dm":"603960","mc":"克来机电","jys":"sh"},{"dm":"603709","mc":"中源家居","jys":"sh"},{"dm":"603006","mc":"联明股份","jys":"sh"},{"dm":"600638","mc":"新黄浦","jys":"sh"},{"dm":"002323","mc":"雅博股份","jys":"sz"},{"dm":"301229","mc":"纽泰格","jys":"sz"},{"dm":"603516","mc":"淳中科技","jys":"sh"},{"dm":"688270","mc":"臻镭科技","jys":"sh"},{"dm":"002290","mc":"禾盛新材","jys":"sz"},{"dm":"688567","mc":"孚能科技","jys":"sh"},{"dm":"688338","mc":"赛科希德","jys":"sh"},{"dm":"002474","mc":"榕基软件","jys":"sz"},{"dm":"601126","mc":"四方股份","jys":"sh"},{"dm":"600261","mc":"阳光照明","jys":"sh"},{"dm":"600198","mc":"大唐电信","jys":"sh"},{"dm":"300382","mc":"斯莱克","jys":"sz"},{"dm":"300017","mc":"网宿科技","jys":"sz"},{"dm":"301487","mc":"盟固利","jys":"sz"},{"dm":"002363","mc":"隆基机械","jys":"sz"},{"dm":"002278","mc":"神开股份","jys":"sz"},{"dm":"688109","mc":"品茗科技","jys":"sh"},{"dm":"300917","mc":"特发服务","jys":"sz"},{"dm":"000691","mc":"亚太实业","jys":"sz"},{"dm":"601288","mc":"农业银行","jys":"sh"},{"dm":"688206","mc":"概伦电子","jys":"sh"},{"dm":"600169","mc":"太原重工","jys":"sh"},{"dm":"002181","mc":"粤 传 媒","jys":"sz"},{"dm":"000413","mc":"东旭光电","jys":"sz"},{"dm":"300072","mc":"海新能科","jys":"sz"},{"dm":"002599","mc":"盛通股份","jys":"sz"},{"dm":"000880","mc":"潍柴重机","jys":"sz"},{"dm":"600526","mc":"菲达环保","jys":"sh"},{"dm":"600135","mc":"乐凯胶片","jys":"sh"},{"dm":"688420","mc":"美腾科技","jys":"sh"},{"dm":"300465","mc":"高伟达","jys":"sz"},{"dm":"688175","mc":"高凌信息","jys":"sh"},{"dm":"300743","mc":"天地数码","jys":"sz"},{"dm":"688062","mc":"迈威生物-U","jys":"sh"},{"dm":"688772","mc":"珠海冠宇","jys":"sh"},{"dm":"300774","mc":"倍杰特","jys":"sz"},{"dm":"301548","mc":"崇德科技","jys":"sz"},{"dm":"600737","mc":"中粮糖业","jys":"sh"},{"dm":"301361","mc":"众智科技","jys":"sz"},{"dm":"600784","mc":"鲁银投资","jys":"sh"},{"dm":"600580","mc":"卧龙电驱","jys":"sh"},{"dm":"600571","mc":"信雅达","jys":"sh"},{"dm":"300563","mc":"神宇股份","jys":"sz"},{"dm":"600272","mc":"开开实业","jys":"sh"},{"dm":"001283","mc":"豪鹏科技","jys":"sz"},{"dm":"300505","mc":"川金诺","jys":"sz"},{"dm":"002574","mc":"明牌珠宝","jys":"sz"},{"dm":"300180","mc":"华峰超纤","jys":"sz"},{"dm":"300099","mc":"尤洛卡","jys":"sz"},{"dm":"301132","mc":"满坤科技","jys":"sz"},{"dm":"688213","mc":"思特威-W","jys":"sh"},{"dm":"300483","mc":"首华燃气","jys":"sz"},{"dm":"688655","mc":"迅捷兴","jys":"sh"},{"dm":"601939","mc":"建设银行","jys":"sh"},{"dm":"002560","mc":"通达股份","jys":"sz"},{"dm":"300119","mc":"瑞普生物","jys":"sz"},{"dm":"301517","mc":"陕西华达","jys":"sz"},{"dm":"600408","mc":"安泰集团","jys":"sh"},{"dm":"002282","mc":"博深股份","jys":"sz"},{"dm":"002270","mc":"华明装备","jys":"sz"},{"dm":"300459","mc":"汤姆猫","jys":"sz"},{"dm":"301286","mc":"侨源股份","jys":"sz"},{"dm":"688400","mc":"凌云光","jys":"sh"},{"dm":"002213","mc":"大为股份","jys":"sz"},{"dm":"688428","mc":"诺诚健华-U","jys":"sh"},{"dm":"603998","mc":"方盛制药","jys":"sh"},{"dm":"000761","mc":"本钢板材","jys":"sz"},{"dm":"603053","mc":"成都燃气","jys":"sh"},{"dm":"000536","mc":"华映科技","jys":"sz"},{"dm":"002431","mc":"棕榈股份","jys":"sz"},{"dm":"300957","mc":"贝泰妮","jys":"sz"},{"dm":"002644","mc":"佛慈制药","jys":"sz"},{"dm":"002863","mc":"今飞凯达","jys":"sz"},{"dm":"002971","mc":"和远气体","jys":"sz"},{"dm":"603580","mc":"艾艾精工","jys":"sh"},{"dm":"300624","mc":"万兴科技","jys":"sz"},{"dm":"300600","mc":"国瑞科技","jys":"sz"},{"dm":"301293","mc":"三博脑科","jys":"sz"},{"dm":"300615","mc":"欣天科技","jys":"sz"},{"dm":"600610","mc":"中毅达","jys":"sh"},{"dm":"002376","mc":"新北洋","jys":"sz"},{"dm":"603081","mc":"大丰实业","jys":"sh"},{"dm":"300070","mc":"碧水源","jys":"sz"},{"dm":"002089","mc":"*ST新海","jys":"sz"},{"dm":"601881","mc":"中国银河","jys":"sh"},{"dm":"301178","mc":"天亿马","jys":"sz"},{"dm":"600072","mc":"中船科技","jys":"sh"},{"dm":"300133","mc":"华策影视","jys":"sz"},{"dm":"300460","mc":"惠伦晶体","jys":"sz"},{"dm":"300437","mc":"清水源","jys":"sz"},{"dm":"002516","mc":"旷达科技","jys":"sz"},{"dm":"002755","mc":"奥赛康","jys":"sz"},{"dm":"605288","mc":"凯迪股份","jys":"sh"},{"dm":"002115","mc":"三维通信","jys":"sz"},{"dm":"002188","mc":"中天服务","jys":"sz"},{"dm":"688401","mc":"路维光电","jys":"sh"},{"dm":"000548","mc":"湖南投资","jys":"sz"},{"dm":"600732","mc":"爱旭股份","jys":"sh"},{"dm":"688592","mc":"司南导航","jys":"sh"},{"dm":"688660","mc":"电气风电","jys":"sh"},{"dm":"300977","mc":"深圳瑞捷","jys":"sz"},{"dm":"600084","mc":"中信尼雅","jys":"sh"},{"dm":"002380","mc":"科远智慧","jys":"sz"},{"dm":"000061","mc":"农 产 品","jys":"sz"},{"dm":"603100","mc":"川仪股份","jys":"sh"},{"dm":"600714","mc":"金瑞矿业","jys":"sh"},{"dm":"600368","mc":"五洲交通","jys":"sh"},{"dm":"301226","mc":"祥明智能","jys":"sz"},{"dm":"600969","mc":"郴电国际","jys":"sh"},{"dm":"600313","mc":"农发种业","jys":"sh"},{"dm":"600757","mc":"长江传媒","jys":"sh"},{"dm":"002655","mc":"共达电声","jys":"sz"},{"dm":"603949","mc":"雪龙集团","jys":"sh"},{"dm":"601360","mc":"三六零","jys":"sh"},{"dm":"600886","mc":"国投电力","jys":"sh"},{"dm":"301329","mc":"信音电子","jys":"sz"},{"dm":"002495","mc":"佳隆股份","jys":"sz"},{"dm":"688500","mc":"*ST慧辰","jys":"sh"},{"dm":"002600","mc":"领益智造","jys":"sz"},{"dm":"300982","mc":"苏文电能","jys":"sz"},{"dm":"603712","mc":"七一二","jys":"sh"},{"dm":"601375","mc":"中原证券","jys":"sh"},{"dm":"605069","mc":"正和生态","jys":"sh"},{"dm":"688299","mc":"长阳科技","jys":"sh"},{"dm":"603270","mc":"金帝股份","jys":"sh"},{"dm":"300538","mc":"同益股份","jys":"sz"},{"dm":"002040","mc":"南 京 港","jys":"sz"},{"dm":"301355","mc":"南王科技","jys":"sz"},{"dm":"000790","mc":"华神科技","jys":"sz"},{"dm":"301081","mc":"严牌股份","jys":"sz"},{"dm":"603033","mc":"三维股份","jys":"sh"},{"dm":"600213","mc":"亚星客车","jys":"sh"},{"dm":"688466","mc":"金科环境","jys":"sh"},{"dm":"300817","mc":"双飞集团","jys":"sz"},{"dm":"300671","mc":"富满微","jys":"sz"},{"dm":"688227","mc":"品高股份","jys":"sh"},{"dm":"600157","mc":"永泰能源","jys":"sh"},{"dm":"002537","mc":"海联金汇","jys":"sz"},{"dm":"300066","mc":"三川智慧","jys":"sz"},{"dm":"605259","mc":"绿田机械","jys":"sh"},{"dm":"002969","mc":"嘉美包装","jys":"sz"},{"dm":"600481","mc":"双良节能","jys":"sh"},{"dm":"300847","mc":"中船汉光","jys":"sz"},{"dm":"002194","mc":"武汉凡谷","jys":"sz"},{"dm":"000659","mc":"珠海中富","jys":"sz"},{"dm":"688127","mc":"蓝特光学","jys":"sh"},{"dm":"000933","mc":"神火股份","jys":"sz"},{"dm":"002629","mc":"仁智股份","jys":"sz"},{"dm":"688060","mc":"云涌科技","jys":"sh"},{"dm":"301314","mc":"科瑞思","jys":"sz"},{"dm":"600277","mc":"亿利洁能","jys":"sh"},{"dm":"002047","mc":"宝鹰股份","jys":"sz"},{"dm":"300468","mc":"四方精创","jys":"sz"},{"dm":"688281","mc":"华秦科技","jys":"sh"},{"dm":"300124","mc":"汇川技术","jys":"sz"},{"dm":"301359","mc":"东南电子","jys":"sz"},{"dm":"300649","mc":"杭州园林","jys":"sz"},{"dm":"300579","mc":"数字认证","jys":"sz"},{"dm":"300663","mc":"科蓝软件","jys":"sz"},{"dm":"600725","mc":"云维股份","jys":"sh"},{"dm":"600279","mc":"重庆港","jys":"sh"},{"dm":"688366","mc":"昊海生科","jys":"sh"},{"dm":"601696","mc":"中银证券","jys":"sh"},{"dm":"002161","mc":"远 望 谷","jys":"sz"},{"dm":"688697","mc":"纽威数控","jys":"sh"},{"dm":"001228","mc":"永泰运","jys":"sz"},{"dm":"301320","mc":"豪江智能","jys":"sz"},{"dm":"603700","mc":"宁水集团","jys":"sh"},{"dm":"002848","mc":"高斯贝尔","jys":"sz"},{"dm":"002799","mc":"环球印务","jys":"sz"},{"dm":"002002","mc":"ST鸿达","jys":"sz"},{"dm":"002527","mc":"新时达","jys":"sz"},{"dm":"002822","mc":"中装建设","jys":"sz"},{"dm":"600551","mc":"时代出版","jys":"sh"},{"dm":"688083","mc":"中望软件","jys":"sh"},{"dm":"600218","mc":"全柴动力","jys":"sh"},{"dm":"000533","mc":"顺钠股份","jys":"sz"},{"dm":"600712","mc":"南宁百货","jys":"sh"},{"dm":"603633","mc":"徕木股份","jys":"sh"},{"dm":"600837","mc":"海通证券","jys":"sh"},{"dm":"002743","mc":"富煌钢构","jys":"sz"},{"dm":"300448","mc":"浩云科技","jys":"sz"},{"dm":"301283","mc":"聚胶股份","jys":"sz"},{"dm":"603803","mc":"瑞斯康达","jys":"sh"},{"dm":"603860","mc":"中公高科","jys":"sh"},{"dm":"002995","mc":"天地在线","jys":"sz"},{"dm":"688689","mc":"银河微电","jys":"sh"},{"dm":"000586","mc":"汇源通信","jys":"sz"},{"dm":"000035","mc":"中国天楹","jys":"sz"},{"dm":"300554","mc":"三超新材","jys":"sz"},{"dm":"300698","mc":"万马科技","jys":"sz"},{"dm":"002032","mc":"苏 泊 尔","jys":"sz"},{"dm":"000702","mc":"正虹科技","jys":"sz"},{"dm":"300751","mc":"迈为股份","jys":"sz"},{"dm":"002069","mc":"獐子岛","jys":"sz"},{"dm":"688450","mc":"光格科技","jys":"sh"},{"dm":"600805","mc":"悦达投资","jys":"sh"},{"dm":"300154","mc":"瑞凌股份","jys":"sz"},{"dm":"002972","mc":"科安达","jys":"sz"},{"dm":"300020","mc":"银江技术","jys":"sz"},{"dm":"605580","mc":"恒盛能源","jys":"sh"},{"dm":"300407","mc":"凯发电气","jys":"sz"},{"dm":"002949","mc":"华阳国际","jys":"sz"},{"dm":"002569","mc":"ST步森","jys":"sz"},{"dm":"000017","mc":"深中华A","jys":"sz"},{"dm":"300305","mc":"裕兴股份","jys":"sz"},{"dm":"002514","mc":"宝馨科技","jys":"sz"},{"dm":"600238","mc":"海南椰岛","jys":"sh"},{"dm":"688391","mc":"钜泉科技","jys":"sh"},{"dm":"601019","mc":"山东出版","jys":"sh"},{"dm":"601878","mc":"浙商证券","jys":"sh"},{"dm":"600894","mc":"广日股份","jys":"sh"},{"dm":"301037","mc":"保立佳","jys":"sz"},{"dm":"300300","mc":"海峡创新","jys":"sz"},{"dm":"002675","mc":"东诚药业","jys":"sz"},{"dm":"000639","mc":"西王食品","jys":"sz"},{"dm":"603127","mc":"昭衍新药","jys":"sh"},{"dm":"002613","mc":"北玻股份","jys":"sz"},{"dm":"000661","mc":"长春高新","jys":"sz"},{"dm":"605179","mc":"一鸣食品","jys":"sh"},{"dm":"601899","mc":"紫金矿业","jys":"sh"},{"dm":"300424","mc":"航新科技","jys":"sz"},{"dm":"688557","mc":"兰剑智能","jys":"sh"},{"dm":"300989","mc":"蕾奥规划","jys":"sz"},{"dm":"603920","mc":"世运电路","jys":"sh"},{"dm":"002401","mc":"中远海科","jys":"sz"},{"dm":"300996","mc":"普联软件","jys":"sz"},{"dm":"002851","mc":"麦格米特","jys":"sz"},{"dm":"688117","mc":"圣诺生物","jys":"sh"},{"dm":"600791","mc":"京能置业","jys":"sh"},{"dm":"300245","mc":"天玑科技","jys":"sz"},{"dm":"600611","mc":"大众交通","jys":"sh"},{"dm":"002299","mc":"圣农发展","jys":"sz"},{"dm":"688603","mc":"天承科技","jys":"sh"},{"dm":"300203","mc":"聚光科技","jys":"sz"},{"dm":"300172","mc":"中电环保","jys":"sz"},{"dm":"600075","mc":"新疆天业","jys":"sh"},{"dm":"002639","mc":"雪人股份","jys":"sz"},{"dm":"301505","mc":"苏州规划","jys":"sz"},{"dm":"000803","mc":"山高环能","jys":"sz"},{"dm":"301067","mc":"显盈科技","jys":"sz"},{"dm":"000409","mc":"云鼎科技","jys":"sz"},{"dm":"300899","mc":"上海凯鑫","jys":"sz"},{"dm":"003041","mc":"真爱美家","jys":"sz"},{"dm":"300140","mc":"节能环境","jys":"sz"},{"dm":"688288","mc":"鸿泉物联","jys":"sh"},{"dm":"301197","mc":"工大科雅","jys":"sz"},{"dm":"603036","mc":"如通股份","jys":"sh"},{"dm":"688699","mc":"明微电子","jys":"sh"},{"dm":"002519","mc":"银河电子","jys":"sz"},{"dm":"300168","mc":"万达信息","jys":"sz"},{"dm":"002633","mc":"申科股份","jys":"sz"},{"dm":"301179","mc":"泽宇智能","jys":"sz"},{"dm":"603061","mc":"金海通","jys":"sh"},{"dm":"688622","mc":"禾信仪器","jys":"sh"},{"dm":"603075","mc":"热威股份","jys":"sh"},{"dm":"688267","mc":"中触媒","jys":"sh"},{"dm":"688517","mc":"金冠电气","jys":"sh"},{"dm":"603535","mc":"嘉诚国际","jys":"sh"},{"dm":"002105","mc":"信隆健康","jys":"sz"},{"dm":"002753","mc":"永东股份","jys":"sz"},{"dm":"605151","mc":"西上海","jys":"sh"},{"dm":"300904","mc":"威力传动","jys":"sz"},{"dm":"002871","mc":"伟隆股份","jys":"sz"},{"dm":"300868","mc":"杰美特","jys":"sz"},{"dm":"603999","mc":"读者传媒","jys":"sh"},{"dm":"300161","mc":"华中数控","jys":"sz"},{"dm":"688485","mc":"九州一轨","jys":"sh"},{"dm":"601921","mc":"浙版传媒","jys":"sh"},{"dm":"301196","mc":"唯科科技","jys":"sz"},{"dm":"000797","mc":"中国武夷","jys":"sz"},{"dm":"600435","mc":"北方导航","jys":"sh"},{"dm":"600698","mc":"湖南天雁","jys":"sh"},{"dm":"600855","mc":"航天长峰","jys":"sh"},{"dm":"600422","mc":"昆药集团","jys":"sh"},{"dm":"300315","mc":"掌趣科技","jys":"sz"},{"dm":"603183","mc":"建研院","jys":"sh"},{"dm":"605376","mc":"博迁新材","jys":"sh"},{"dm":"300489","mc":"光智科技","jys":"sz"},{"dm":"300879","mc":"大叶股份","jys":"sz"},{"dm":"688339","mc":"亿华通-U","jys":"sh"},{"dm":"002956","mc":"西麦食品","jys":"sz"},{"dm":"688509","mc":"正元地信","jys":"sh"},{"dm":"300679","mc":"电连技术","jys":"sz"},{"dm":"002810","mc":"山东赫达","jys":"sz"},{"dm":"300282","mc":"*ST三盛","jys":"sz"},{"dm":"002996","mc":"顺博合金","jys":"sz"},{"dm":"688700","mc":"东威科技","jys":"sh"},{"dm":"300252","mc":"金信诺","jys":"sz"},{"dm":"300955","mc":"嘉亨家化","jys":"sz"},{"dm":"603738","mc":"泰晶科技","jys":"sh"},{"dm":"600872","mc":"中炬高新","jys":"sh"},{"dm":"688519","mc":"南亚新材","jys":"sh"},{"dm":"603333","mc":"尚纬股份","jys":"sh"},{"dm":"601995","mc":"中金公司","jys":"sh"},{"dm":"002926","mc":"华西证券","jys":"sz"},{"dm":"300351","mc":"永贵电器","jys":"sz"},{"dm":"688506","mc":"百利天恒-U","jys":"sh"},{"dm":"603058","mc":"永吉股份","jys":"sh"},{"dm":"300967","mc":"晓鸣股份","jys":"sz"},{"dm":"603595","mc":"东尼电子","jys":"sh"},{"dm":"300925","mc":"法本信息","jys":"sz"},{"dm":"001696","mc":"宗申动力","jys":"sz"},{"dm":"000529","mc":"广弘控股","jys":"sz"},{"dm":"605018","mc":"长华集团","jys":"sh"},{"dm":"600833","mc":"第一医药","jys":"sh"},{"dm":"603655","mc":"朗博科技","jys":"sh"},{"dm":"300882","mc":"万胜智能","jys":"sz"},{"dm":"300672","mc":"国科微","jys":"sz"},{"dm":"600667","mc":"太极实业","jys":"sh"},{"dm":"002006","mc":"精工科技","jys":"sz"},{"dm":"603608","mc":"天创时尚","jys":"sh"},{"dm":"002621","mc":"美吉姆","jys":"sz"},{"dm":"301376","mc":"致欧科技","jys":"sz"},{"dm":"688197","mc":"首药控股-U","jys":"sh"},{"dm":"300695","mc":"兆丰股份","jys":"sz"},{"dm":"600865","mc":"百大集团","jys":"sh"},{"dm":"600770","mc":"综艺股份","jys":"sh"},{"dm":"603536","mc":"惠发食品","jys":"sh"},{"dm":"603258","mc":"电魂网络","jys":"sh"},{"dm":"002440","mc":"闰土股份","jys":"sz"},{"dm":"002487","mc":"大金重工","jys":"sz"},{"dm":"300824","mc":"北鼎股份","jys":"sz"},{"dm":"300321","mc":"同大股份","jys":"sz"},{"dm":"300611","mc":"美力科技","jys":"sz"},{"dm":"000948","mc":"南天信息","jys":"sz"},{"dm":"600609","mc":"金杯汽车","jys":"sh"},{"dm":"300690","mc":"双一科技","jys":"sz"},{"dm":"000860","mc":"顺鑫农业","jys":"sz"},{"dm":"002156","mc":"通富微电","jys":"sz"},{"dm":"605011","mc":"杭州热电","jys":"sh"},{"dm":"601898","mc":"中煤能源","jys":"sh"},{"dm":"300875","mc":"捷强装备","jys":"sz"},{"dm":"600160","mc":"巨化股份","jys":"sh"},{"dm":"300814","mc":"中富电路","jys":"sz"},{"dm":"002398","mc":"垒知集团","jys":"sz"},{"dm":"003017","mc":"大洋生物","jys":"sz"},{"dm":"301160","mc":"翔楼新材","jys":"sz"},{"dm":"001217","mc":"华尔泰","jys":"sz"},{"dm":"600854","mc":"春兰股份","jys":"sh"},{"dm":"300630","mc":"普利制药","jys":"sz"},{"dm":"605098","mc":"行动教育","jys":"sh"},{"dm":"300826","mc":"测绘股份","jys":"sz"},{"dm":"301260","mc":"格力博","jys":"sz"},{"dm":"601928","mc":"凤凰传媒","jys":"sh"},{"dm":"300348","mc":"长亮科技","jys":"sz"},{"dm":"002723","mc":"小崧股份","jys":"sz"},{"dm":"603819","mc":"神力股份","jys":"sh"},{"dm":"601018","mc":"宁波港","jys":"sh"},{"dm":"603759","mc":"海天股份","jys":"sh"},{"dm":"600753","mc":"庚星股份","jys":"sh"},{"dm":"002939","mc":"长城证券","jys":"sz"},{"dm":"601113","mc":"华鼎股份","jys":"sh"},{"dm":"001211","mc":"双枪科技","jys":"sz"},{"dm":"601133","mc":"柏诚股份","jys":"sh"},{"dm":"301378","mc":"通达海","jys":"sz"},{"dm":"300352","mc":"北信源","jys":"sz"},{"dm":"000983","mc":"山西焦煤","jys":"sz"},{"dm":"000895","mc":"双汇发展","jys":"sz"},{"dm":"000510","mc":"新金路","jys":"sz"},{"dm":"603106","mc":"恒银科技","jys":"sh"},{"dm":"300647","mc":"超频三","jys":"sz"},{"dm":"688565","mc":"力源科技","jys":"sh"},{"dm":"002728","mc":"特一药业","jys":"sz"},{"dm":"002605","mc":"姚记科技","jys":"sz"},{"dm":"688063","mc":"派能科技","jys":"sh"},{"dm":"002806","mc":"华锋股份","jys":"sz"},{"dm":"301059","mc":"金三江","jys":"sz"},{"dm":"600267","mc":"海正药业","jys":"sh"},{"dm":"601088","mc":"中国神华","jys":"sh"},{"dm":"300810","mc":"中科海讯","jys":"sz"},{"dm":"603117","mc":"ST万林","jys":"sh"},{"dm":"601168","mc":"西部矿业","jys":"sh"},{"dm":"003016","mc":"欣贺股份","jys":"sz"},{"dm":"600699","mc":"均胜电子","jys":"sh"},{"dm":"601798","mc":"蓝科高新","jys":"sh"},{"dm":"000555","mc":"神州信息","jys":"sz"},{"dm":"003032","mc":"传智教育","jys":"sz"},{"dm":"000981","mc":"山子股份","jys":"sz"},{"dm":"300581","mc":"晨曦航空","jys":"sz"},{"dm":"000779","mc":"甘咨询","jys":"sz"},{"dm":"600675","mc":"中华企业","jys":"sh"},{"dm":"601900","mc":"南方传媒","jys":"sh"},{"dm":"300482","mc":"万孚生物","jys":"sz"},{"dm":"301082","mc":"久盛电气","jys":"sz"},{"dm":"600928","mc":"西安银行","jys":"sh"},{"dm":"300129","mc":"泰胜风能","jys":"sz"},{"dm":"002719","mc":"麦趣尔","jys":"sz"},{"dm":"002036","mc":"联创电子","jys":"sz"},{"dm":"001231","mc":"农心科技","jys":"sz"},{"dm":"300836","mc":"佰奥智能","jys":"sz"},{"dm":"002809","mc":"红墙股份","jys":"sz"},{"dm":"603528","mc":"多伦科技","jys":"sh"},{"dm":"000655","mc":"金岭矿业","jys":"sz"},{"dm":"300660","mc":"江苏雷利","jys":"sz"},{"dm":"002343","mc":"慈文传媒","jys":"sz"},{"dm":"600363","mc":"联创光电","jys":"sh"},{"dm":"300747","mc":"锐科激光","jys":"sz"},{"dm":"002823","mc":"凯中精密","jys":"sz"},{"dm":"605378","mc":"野马电池","jys":"sh"},{"dm":"300132","mc":"青松股份","jys":"sz"},{"dm":"300469","mc":"信息发展","jys":"sz"},{"dm":"000703","mc":"恒逸石化","jys":"sz"},{"dm":"600756","mc":"浪潮软件","jys":"sh"},{"dm":"300082","mc":"奥克股份","jys":"sz"},{"dm":"002970","mc":"锐明技术","jys":"sz"},{"dm":"001267","mc":"汇绿生态","jys":"sz"},{"dm":"688348","mc":"昱能科技","jys":"sh"},{"dm":"002264","mc":"新 华 都","jys":"sz"},{"dm":"301031","mc":"中熔电气","jys":"sz"},{"dm":"000063","mc":"中兴通讯","jys":"sz"},{"dm":"300801","mc":"泰和科技","jys":"sz"},{"dm":"002910","mc":"庄园牧场","jys":"sz"},{"dm":"300065","mc":"海兰信","jys":"sz"},{"dm":"002455","mc":"百川股份","jys":"sz"},{"dm":"300811","mc":"铂科新材","jys":"sz"},{"dm":"600822","mc":"上海物贸","jys":"sh"},{"dm":"002789","mc":"建艺集团","jys":"sz"},{"dm":"000019","mc":"深粮控股","jys":"sz"},{"dm":"300718","mc":"长盛轴承","jys":"sz"},{"dm":"688627","mc":"精智达","jys":"sh"},{"dm":"300444","mc":"双杰电气","jys":"sz"},{"dm":"688331","mc":"荣昌生物","jys":"sh"},{"dm":"688199","mc":"久日新材","jys":"sh"},{"dm":"301107","mc":"瑜欣电子","jys":"sz"},{"dm":"600399","mc":"抚顺特钢","jys":"sh"},{"dm":"002601","mc":"龙佰集团","jys":"sz"},{"dm":"688608","mc":"恒玄科技","jys":"sh"},{"dm":"600249","mc":"两面针","jys":"sh"},{"dm":"600107","mc":"美尔雅","jys":"sh"},{"dm":"688161","mc":"威高骨科","jys":"sh"},{"dm":"301362","mc":"民爆光电","jys":"sz"},{"dm":"605196","mc":"华通线缆","jys":"sh"},{"dm":"001208","mc":"华菱线缆","jys":"sz"},{"dm":"002185","mc":"华天科技","jys":"sz"},{"dm":"002730","mc":"电光科技","jys":"sz"},{"dm":"002129","mc":"TCL中环","jys":"sz"},{"dm":"688037","mc":"芯源微","jys":"sh"},{"dm":"002861","mc":"瀛通通讯","jys":"sz"},{"dm":"301155","mc":"海力风电","jys":"sz"},{"dm":"600234","mc":"科新发展","jys":"sh"},{"dm":"300906","mc":"日月明","jys":"sz"},{"dm":"002374","mc":"中锐股份","jys":"sz"},{"dm":"688015","mc":"交控科技","jys":"sh"},{"dm":"002660","mc":"茂硕电源","jys":"sz"},{"dm":"301386","mc":"未来电器","jys":"sz"},{"dm":"600188","mc":"兖矿能源","jys":"sh"},{"dm":"603507","mc":"振江股份","jys":"sh"},{"dm":"000151","mc":"中成股份","jys":"sz"},{"dm":"300071","mc":"福石控股","jys":"sz"},{"dm":"300542","mc":"新晨科技","jys":"sz"},{"dm":"002614","mc":"奥佳华","jys":"sz"},{"dm":"300412","mc":"迦南科技","jys":"sz"},{"dm":"000921","mc":"海信家电","jys":"sz"},{"dm":"001324","mc":"长青科技","jys":"sz"},{"dm":"300655","mc":"晶瑞电材","jys":"sz"},{"dm":"301213","mc":"观想科技","jys":"sz"},{"dm":"300688","mc":"创业黑马","jys":"sz"},{"dm":"688398","mc":"赛特新材","jys":"sh"},{"dm":"002980","mc":"华盛昌","jys":"sz"},{"dm":"603228","mc":"景旺电子","jys":"sh"},{"dm":"300040","mc":"九洲集团","jys":"sz"},{"dm":"601608","mc":"中信重工","jys":"sh"},{"dm":"601011","mc":"宝泰隆","jys":"sh"},{"dm":"300212","mc":"易华录","jys":"sz"},{"dm":"600990","mc":"四创电子","jys":"sh"},{"dm":"300586","mc":"美联新材","jys":"sz"},{"dm":"601777","mc":"力帆科技","jys":"sh"},{"dm":"000156","mc":"华数传媒","jys":"sz"},{"dm":"688682","mc":"霍莱沃","jys":"sh"},{"dm":"002575","mc":"群兴玩具","jys":"sz"},{"dm":"000735","mc":"罗 牛 山","jys":"sz"},{"dm":"600109","mc":"国金证券","jys":"sh"},{"dm":"601218","mc":"吉鑫科技","jys":"sh"},{"dm":"688139","mc":"海尔生物","jys":"sh"},{"dm":"000036","mc":"华联控股","jys":"sz"},{"dm":"300993","mc":"玉马遮阳","jys":"sz"},{"dm":"002819","mc":"东方中科","jys":"sz"},{"dm":"688106","mc":"金宏气体","jys":"sh"},{"dm":"300940","mc":"南极光","jys":"sz"},{"dm":"601811","mc":"新华文轩","jys":"sh"},{"dm":"600560","mc":"金自天正","jys":"sh"},{"dm":"002500","mc":"山西证券","jys":"sz"},{"dm":"000850","mc":"华茂股份","jys":"sz"},{"dm":"688234","mc":"天岳先进","jys":"sh"},{"dm":"002381","mc":"双箭股份","jys":"sz"},{"dm":"603798","mc":"康普顿","jys":"sh"},{"dm":"301373","mc":"凌玮科技","jys":"sz"},{"dm":"603331","mc":"百达精工","jys":"sh"},{"dm":"600235","mc":"民丰特纸","jys":"sh"},{"dm":"002913","mc":"奥士康","jys":"sz"},{"dm":"300862","mc":"蓝盾光电","jys":"sz"},{"dm":"600356","mc":"恒丰纸业","jys":"sh"},{"dm":"002093","mc":"国脉科技","jys":"sz"},{"dm":"688586","mc":"江航装备","jys":"sh"},{"dm":"603903","mc":"中持股份","jys":"sh"},{"dm":"300346","mc":"南大光电","jys":"sz"},{"dm":"603018","mc":"华设集团","jys":"sh"},{"dm":"000833","mc":"粤桂股份","jys":"sz"},{"dm":"601007","mc":"金陵饭店","jys":"sh"},{"dm":"300952","mc":"恒辉安防","jys":"sz"},{"dm":"600379","mc":"宝光股份","jys":"sh"},{"dm":"688251","mc":"井松智能","jys":"sh"},{"dm":"301150","mc":"中一科技","jys":"sz"},{"dm":"300845","mc":"捷安高科","jys":"sz"},{"dm":"603520","mc":"司太立","jys":"sh"},{"dm":"605089","mc":"味知香","jys":"sh"},{"dm":"000636","mc":"风华高科","jys":"sz"},{"dm":"300700","mc":"岱勒新材","jys":"sz"},{"dm":"688395","mc":"正弦电气","jys":"sh"},{"dm":"603290","mc":"斯达半导","jys":"sh"},{"dm":"603022","mc":"新通联","jys":"sh"},{"dm":"301233","mc":"盛帮股份","jys":"sz"},{"dm":"600850","mc":"电科数字","jys":"sh"},{"dm":"002877","mc":"智能自控","jys":"sz"},{"dm":"300670","mc":"大烨智能","jys":"sz"},{"dm":"605117","mc":"德业股份","jys":"sh"},{"dm":"603261","mc":"立航科技","jys":"sh"},{"dm":"002042","mc":"华孚时尚","jys":"sz"},{"dm":"688116","mc":"天奈科技","jys":"sh"},{"dm":"600421","mc":"华嵘控股","jys":"sh"},{"dm":"688056","mc":"莱伯泰科","jys":"sh"},{"dm":"600386","mc":"北巴传媒","jys":"sh"},{"dm":"301199","mc":"迈赫股份","jys":"sz"},{"dm":"000701","mc":"厦门信达","jys":"sz"},{"dm":"000338","mc":"潍柴动力","jys":"sz"},{"dm":"603066","mc":"音飞储存","jys":"sh"},{"dm":"300449","mc":"汉邦高科","jys":"sz"},{"dm":"603596","mc":"伯特利","jys":"sh"},{"dm":"600906","mc":"财达证券","jys":"sh"},{"dm":"300165","mc":"天瑞仪器","jys":"sz"},{"dm":"002830","mc":"名雕股份","jys":"sz"},{"dm":"000400","mc":"许继电气","jys":"sz"},{"dm":"000851","mc":"高鸿股份","jys":"sz"},{"dm":"000408","mc":"藏格矿业","jys":"sz"},{"dm":"002625","mc":"光启技术","jys":"sz"},{"dm":"300640","mc":"德艺文创","jys":"sz"},{"dm":"688390","mc":"固德威","jys":"sh"},{"dm":"603636","mc":"南威软件","jys":"sh"},{"dm":"300746","mc":"汉嘉设计","jys":"sz"},{"dm":"002364","mc":"中恒电气","jys":"sz"},{"dm":"688380","mc":"中微半导","jys":"sh"},{"dm":"300518","mc":"新迅达","jys":"sz"},{"dm":"002588","mc":"史丹利","jys":"sz"},{"dm":"002021","mc":"*ST中捷","jys":"sz"},{"dm":"603088","mc":"宁波精达","jys":"sh"},{"dm":"301291","mc":"明阳电气","jys":"sz"},{"dm":"002344","mc":"海宁皮城","jys":"sz"},{"dm":"603182","mc":"嘉华股份","jys":"sh"},{"dm":"000698","mc":"沈阳化工","jys":"sz"},{"dm":"300095","mc":"华伍股份","jys":"sz"},{"dm":"605005","mc":"合兴股份","jys":"sh"},{"dm":"600126","mc":"杭钢股份","jys":"sh"},{"dm":"301137","mc":"哈焊华通","jys":"sz"},{"dm":"002637","mc":"赞宇科技","jys":"sz"},{"dm":"300136","mc":"信维通信","jys":"sz"},{"dm":"600220","mc":"江苏阳光","jys":"sh"},{"dm":"002689","mc":"远大智能","jys":"sz"},{"dm":"300531","mc":"优博讯","jys":"sz"},{"dm":"002236","mc":"大华股份","jys":"sz"},{"dm":"600676","mc":"交运股份","jys":"sh"},{"dm":"600962","mc":"国投中鲁","jys":"sh"},{"dm":"688528","mc":"秦川物联","jys":"sh"},{"dm":"000403","mc":"派林生物","jys":"sz"},{"dm":"002534","mc":"西子洁能","jys":"sz"},{"dm":"301193","mc":"家联科技","jys":"sz"},{"dm":"688768","mc":"容知日新","jys":"sh"},{"dm":"600881","mc":"亚泰集团","jys":"sh"},{"dm":"002221","mc":"东华能源","jys":"sz"},{"dm":"300190","mc":"维尔利","jys":"sz"},{"dm":"300008","mc":"天海防务","jys":"sz"},{"dm":"002072","mc":"凯瑞德","jys":"sz"},{"dm":"600203","mc":"福日电子","jys":"sh"},{"dm":"603893","mc":"瑞芯微","jys":"sh"},{"dm":"600996","mc":"贵广网络","jys":"sh"},{"dm":"300759","mc":"康龙化成","jys":"sz"},{"dm":"600590","mc":"泰豪科技","jys":"sh"},{"dm":"300295","mc":"三六五网","jys":"sz"},{"dm":"688165","mc":"埃夫特-U","jys":"sh"},{"dm":"300673","mc":"佩蒂股份","jys":"sz"},{"dm":"600797","mc":"浙大网新","jys":"sh"},{"dm":"300411","mc":"金盾股份","jys":"sz"},{"dm":"603938","mc":"三孚股份","jys":"sh"},{"dm":"688665","mc":"四方光电","jys":"sh"},{"dm":"002302","mc":"西部建设","jys":"sz"},{"dm":"000557","mc":"西部创业","jys":"sz"},{"dm":"000532","mc":"华金资本","jys":"sz"},{"dm":"688597","mc":"煜邦电力","jys":"sh"},{"dm":"000430","mc":"张家界","jys":"sz"},{"dm":"001311","mc":"多利科技","jys":"sz"},{"dm":"600318","mc":"新力金融","jys":"sh"},{"dm":"002208","mc":"合肥城建","jys":"sz"},{"dm":"301318","mc":"维海德","jys":"sz"},{"dm":"002449","mc":"国星光电","jys":"sz"},{"dm":"002923","mc":"润都股份","jys":"sz"},{"dm":"002833","mc":"弘亚数控","jys":"sz"},{"dm":"000913","mc":"钱江摩托","jys":"sz"},{"dm":"300047","mc":"天源迪科","jys":"sz"},{"dm":"300479","mc":"神思电子","jys":"sz"},{"dm":"002617","mc":"露笑科技","jys":"sz"},{"dm":"002144","mc":"宏达高科","jys":"sz"},{"dm":"688981","mc":"中芯国际","jys":"sh"},{"dm":"301519","mc":"舜禹股份","jys":"sz"},{"dm":"002866","mc":"传艺科技","jys":"sz"},{"dm":"603717","mc":"天域生态","jys":"sh"},{"dm":"002155","mc":"湖南黄金","jys":"sz"},{"dm":"600444","mc":"国机通用","jys":"sh"},{"dm":"603408","mc":"建霖家居","jys":"sh"},{"dm":"603101","mc":"汇嘉时代","jys":"sh"},{"dm":"600278","mc":"东方创业","jys":"sh"},{"dm":"688135","mc":"利扬芯片","jys":"sh"},{"dm":"603790","mc":"雅运股份","jys":"sh"},{"dm":"002841","mc":"视源股份","jys":"sz"},{"dm":"000723","mc":"美锦能源","jys":"sz"},{"dm":"688092","mc":"爱科科技","jys":"sh"},{"dm":"688315","mc":"诺禾致源","jys":"sh"},{"dm":"300724","mc":"捷佳伟创","jys":"sz"},{"dm":"688656","mc":"浩欧博","jys":"sh"},{"dm":"603600","mc":"永艺股份","jys":"sh"},{"dm":"301148","mc":"嘉戎技术","jys":"sz"},{"dm":"603288","mc":"海天味业","jys":"sh"},{"dm":"688271","mc":"联影医疗","jys":"sh"},{"dm":"603069","mc":"海汽集团","jys":"sh"},{"dm":"001225","mc":"和泰机电","jys":"sz"},{"dm":"603499","mc":"翔港科技","jys":"sh"},{"dm":"000008","mc":"神州高铁","jys":"sz"},{"dm":"002267","mc":"陕天然气","jys":"sz"},{"dm":"000877","mc":"天山股份","jys":"sz"},{"dm":"600152","mc":"维科技术","jys":"sh"},{"dm":"603133","mc":"*ST碳元","jys":"sh"},{"dm":"688659","mc":"元琛科技","jys":"sh"},{"dm":"600327","mc":"大东方","jys":"sh"},{"dm":"300860","mc":"锋尚文化","jys":"sz"},{"dm":"001323","mc":"慕思股份","jys":"sz"},{"dm":"002890","mc":"弘宇股份","jys":"sz"},{"dm":"603696","mc":"安记食品","jys":"sh"},{"dm":"600736","mc":"苏州高新","jys":"sh"},{"dm":"300238","mc":"冠昊生物","jys":"sz"},{"dm":"300243","mc":"瑞丰高材","jys":"sz"},{"dm":"300756","mc":"金马游乐","jys":"sz"},{"dm":"300865","mc":"大宏立","jys":"sz"},{"dm":"688089","mc":"嘉必优","jys":"sh"},{"dm":"688350","mc":"富淼科技","jys":"sh"},{"dm":"000901","mc":"航天科技","jys":"sz"},{"dm":"002079","mc":"苏州固锝","jys":"sz"},{"dm":"301076","mc":"新瀚新材","jys":"sz"},{"dm":"300706","mc":"阿石创","jys":"sz"},{"dm":"002095","mc":"生 意 宝","jys":"sz"},{"dm":"000715","mc":"中兴商业","jys":"sz"},{"dm":"600210","mc":"紫江企业","jys":"sh"},{"dm":"002746","mc":"仙坛股份","jys":"sz"},{"dm":"301000","mc":"肇民科技","jys":"sz"},{"dm":"603068","mc":"博通集成","jys":"sh"},{"dm":"600819","mc":"耀皮玻璃","jys":"sh"},{"dm":"300852","mc":"四会富仕","jys":"sz"},{"dm":"301138","mc":"华研精机","jys":"sz"},{"dm":"300222","mc":"科大智能","jys":"sz"},{"dm":"002999","mc":"天禾股份","jys":"sz"},{"dm":"603968","mc":"醋化股份","jys":"sh"},{"dm":"603815","mc":"交建股份","jys":"sh"},{"dm":"600103","mc":"青山纸业","jys":"sh"},{"dm":"300402","mc":"宝色股份","jys":"sz"},{"dm":"000812","mc":"陕西金叶","jys":"sz"},{"dm":"603931","mc":"格林达","jys":"sh"},{"dm":"601010","mc":"文峰股份","jys":"sh"},{"dm":"688244","mc":"永信至诚","jys":"sh"},{"dm":"001366","mc":"播恩集团","jys":"sz"},{"dm":"300786","mc":"国林科技","jys":"sz"},{"dm":"002603","mc":"以岭药业","jys":"sz"},{"dm":"002645","mc":"华宏科技","jys":"sz"},{"dm":"002783","mc":"凯龙股份","jys":"sz"},{"dm":"002666","mc":"德联集团","jys":"sz"},{"dm":"600637","mc":"东方明珠","jys":"sh"},{"dm":"603041","mc":"美思德","jys":"sh"},{"dm":"600893","mc":"航发动力","jys":"sh"},{"dm":"300458","mc":"全志科技","jys":"sz"},{"dm":"603227","mc":"雪峰科技","jys":"sh"},{"dm":"300527","mc":"中船应急","jys":"sz"},{"dm":"688069","mc":"德林海","jys":"sh"},{"dm":"600259","mc":"广晟有色","jys":"sh"},{"dm":"600777","mc":"新潮能源","jys":"sh"},{"dm":"600829","mc":"人民同泰","jys":"sh"},{"dm":"688016","mc":"心脉医疗","jys":"sh"},{"dm":"600540","mc":"新赛股份","jys":"sh"},{"dm":"000090","mc":"天健集团","jys":"sz"},{"dm":"301056","mc":"森赫股份","jys":"sz"},{"dm":"002158","mc":"汉钟精机","jys":"sz"},{"dm":"300966","mc":"共同药业","jys":"sz"},{"dm":"300787","mc":"海能实业","jys":"sz"},{"dm":"688193","mc":"仁度生物","jys":"sh"},{"dm":"301303","mc":"真兰仪表","jys":"sz"},{"dm":"600636","mc":"国新文化","jys":"sh"},{"dm":"600512","mc":"腾达建设","jys":"sh"},{"dm":"002542","mc":"中化岩土","jys":"sz"},{"dm":"002620","mc":"瑞和股份","jys":"sz"},{"dm":"002383","mc":"合众思壮","jys":"sz"},{"dm":"300438","mc":"鹏辉能源","jys":"sz"},{"dm":"000830","mc":"鲁西化工","jys":"sz"},{"dm":"300656","mc":"民德电子","jys":"sz"},{"dm":"600348","mc":"华阳股份","jys":"sh"},{"dm":"002502","mc":"ST鼎龙","jys":"sz"},{"dm":"603687","mc":"大胜达","jys":"sh"},{"dm":"301192","mc":"泰祥股份","jys":"sz"},{"dm":"301156","mc":"美农生物","jys":"sz"},{"dm":"300234","mc":"开尔新材","jys":"sz"},{"dm":"688203","mc":"海正生材","jys":"sh"},{"dm":"301510","mc":"固高科技","jys":"sz"},{"dm":"603399","mc":"吉翔股份","jys":"sh"},{"dm":"600643","mc":"爱建集团","jys":"sh"},{"dm":"688469","mc":"中芯集成-U","jys":"sh"},{"dm":"000600","mc":"建投能源","jys":"sz"},{"dm":"000989","mc":"九 芝 堂","jys":"sz"},{"dm":"002616","mc":"长青集团","jys":"sz"},{"dm":"688133","mc":"泰坦科技","jys":"sh"},{"dm":"002241","mc":"歌尔股份","jys":"sz"},{"dm":"688335","mc":"复洁环保","jys":"sh"},{"dm":"002869","mc":"金溢科技","jys":"sz"},{"dm":"605589","mc":"圣泉集团","jys":"sh"},{"dm":"300901","mc":"中胤时尚","jys":"sz"},{"dm":"688101","mc":"三达膜","jys":"sh"},{"dm":"300886","mc":"华业香料","jys":"sz"},{"dm":"301028","mc":"东亚机械","jys":"sz"},{"dm":"688038","mc":"中科通达","jys":"sh"},{"dm":"002901","mc":"大博医疗","jys":"sz"},{"dm":"600371","mc":"万向德农","jys":"sh"},{"dm":"300508","mc":"维宏股份","jys":"sz"},{"dm":"002153","mc":"石基信息","jys":"sz"},{"dm":"605366","mc":"宏柏新材","jys":"sh"},{"dm":"600678","mc":"四川金顶","jys":"sh"},{"dm":"002104","mc":"恒宝股份","jys":"sz"},{"dm":"300097","mc":"智云股份","jys":"sz"},{"dm":"600195","mc":"中牧股份","jys":"sh"},{"dm":"002832","mc":"比音勒芬","jys":"sz"},{"dm":"603085","mc":"天成自控","jys":"sh"},{"dm":"000066","mc":"中国长城","jys":"sz"},{"dm":"000990","mc":"诚志股份","jys":"sz"},{"dm":"002523","mc":"天桥起重","jys":"sz"},{"dm":"002718","mc":"友邦吊顶","jys":"sz"},{"dm":"301273","mc":"瑞晨环保","jys":"sz"},{"dm":"688381","mc":"帝奥微","jys":"sh"},{"dm":"002559","mc":"亚威股份","jys":"sz"},{"dm":"300984","mc":"金沃股份","jys":"sz"},{"dm":"603963","mc":"大理药业","jys":"sh"},{"dm":"300641","mc":"正丹股份","jys":"sz"},{"dm":"000612","mc":"焦作万方","jys":"sz"},{"dm":"688479","mc":"友车科技","jys":"sh"},{"dm":"002014","mc":"永新股份","jys":"sz"},{"dm":"600439","mc":"瑞贝卡","jys":"sh"},{"dm":"002945","mc":"华林证券","jys":"sz"},{"dm":"002330","mc":"得利斯","jys":"sz"},{"dm":"002164","mc":"宁波东力","jys":"sz"},{"dm":"600271","mc":"航天信息","jys":"sh"},{"dm":"001286","mc":"陕西能源","jys":"sz"},{"dm":"688728","mc":"格科微","jys":"sh"},{"dm":"300187","mc":"永清环保","jys":"sz"},{"dm":"601101","mc":"昊华能源","jys":"sh"},{"dm":"600844","mc":"丹化科技","jys":"sh"},{"dm":"301295","mc":"美硕科技","jys":"sz"},{"dm":"001227","mc":"兰州银行","jys":"sz"},{"dm":"603882","mc":"金域医学","jys":"sh"},{"dm":"002646","mc":"天佑德酒","jys":"sz"},{"dm":"000783","mc":"长江证券","jys":"sz"},{"dm":"300191","mc":"潜能恒信","jys":"sz"},{"dm":"601956","mc":"东贝集团","jys":"sh"},{"dm":"300849","mc":"锦盛新材","jys":"sz"},{"dm":"300665","mc":"飞鹿股份","jys":"sz"},{"dm":"600397","mc":"安源煤业","jys":"sh"},{"dm":"002586","mc":"*ST围海","jys":"sz"},{"dm":"300125","mc":"聆达股份","jys":"sz"},{"dm":"688367","mc":"工大高科","jys":"sh"},{"dm":"300152","mc":"新动力","jys":"sz"},{"dm":"301120","mc":"新特电气","jys":"sz"},{"dm":"601633","mc":"长城汽车","jys":"sh"},{"dm":"301176","mc":"逸豪新材","jys":"sz"},{"dm":"002492","mc":"恒基达鑫","jys":"sz"},{"dm":"301108","mc":"洁雅股份","jys":"sz"},{"dm":"301330","mc":"熵基科技","jys":"sz"},{"dm":"300311","mc":"任子行","jys":"sz"},{"dm":"300359","mc":"全通教育","jys":"sz"},{"dm":"002717","mc":"岭南股份","jys":"sz"},{"dm":"300566","mc":"激智科技","jys":"sz"},{"dm":"688599","mc":"天合光能","jys":"sh"},{"dm":"300740","mc":"水羊股份","jys":"sz"},{"dm":"603339","mc":"四方科技","jys":"sh"},{"dm":"003042","mc":"中农联合","jys":"sz"},{"dm":"300892","mc":"品渥食品","jys":"sz"},{"dm":"002044","mc":"美年健康","jys":"sz"},{"dm":"300675","mc":"建科院","jys":"sz"},{"dm":"301215","mc":"中汽股份","jys":"sz"},{"dm":"301011","mc":"华立科技","jys":"sz"},{"dm":"000605","mc":"渤海股份","jys":"sz"},{"dm":"603630","mc":"拉芳家化","jys":"sh"},{"dm":"600184","mc":"光电股份","jys":"sh"},{"dm":"300420","mc":"五洋停车","jys":"sz"},{"dm":"300514","mc":"友讯达","jys":"sz"},{"dm":"300224","mc":"正海磁材","jys":"sz"},{"dm":"603112","mc":"华翔股份","jys":"sh"},{"dm":"300422","mc":"博世科","jys":"sz"},{"dm":"600633","mc":"浙数文化","jys":"sh"},{"dm":"300947","mc":"德必集团","jys":"sz"},{"dm":"300145","mc":"中金环境","jys":"sz"},{"dm":"300087","mc":"荃银高科","jys":"sz"},{"dm":"002328","mc":"新朋股份","jys":"sz"},{"dm":"300067","mc":"安诺其","jys":"sz"},{"dm":"000816","mc":"智慧农业","jys":"sz"},{"dm":"605208","mc":"永茂泰","jys":"sh"},{"dm":"688369","mc":"致远互联","jys":"sh"},{"dm":"300262","mc":"巴安水务","jys":"sz"},{"dm":"002721","mc":"*ST金一","jys":"sz"},{"dm":"000545","mc":"金浦钛业","jys":"sz"},{"dm":"003011","mc":"海象新材","jys":"sz"},{"dm":"002556","mc":"辉隆股份","jys":"sz"},{"dm":"002314","mc":"南山控股","jys":"sz"},{"dm":"301163","mc":"宏德股份","jys":"sz"},{"dm":"300195","mc":"长荣股份","jys":"sz"},{"dm":"000716","mc":"黑芝麻","jys":"sz"},{"dm":"000513","mc":"丽珠集团","jys":"sz"},{"dm":"000908","mc":"景峰医药","jys":"sz"},{"dm":"600033","mc":"福建高速","jys":"sh"},{"dm":"003036","mc":"泰坦股份","jys":"sz"},{"dm":"300384","mc":"三联虹普","jys":"sz"},{"dm":"600284","mc":"浦东建设","jys":"sh"},{"dm":"000523","mc":"广州浪奇","jys":"sz"},{"dm":"603869","mc":"新智认知","jys":"sh"},{"dm":"002132","mc":"恒星科技","jys":"sz"},{"dm":"301010","mc":"晶雪节能","jys":"sz"},{"dm":"688013","mc":"天臣医疗","jys":"sh"},{"dm":"300876","mc":"蒙泰高新","jys":"sz"},{"dm":"000157","mc":"中联重科","jys":"sz"},{"dm":"002544","mc":"普天科技","jys":"sz"},{"dm":"600846","mc":"同济科技","jys":"sh"},{"dm":"001216","mc":"华瓷股份","jys":"sz"},{"dm":"605033","mc":"美邦股份","jys":"sh"},{"dm":"301287","mc":"康力源","jys":"sz"},{"dm":"688455","mc":"科捷智能","jys":"sh"},{"dm":"003022","mc":"联泓新科","jys":"sz"},{"dm":"688172","mc":"燕东微","jys":"sh"},{"dm":"300114","mc":"中航电测","jys":"sz"},{"dm":"002092","mc":"中泰化学","jys":"sz"},{"dm":"300692","mc":"中环环保","jys":"sz"},{"dm":"300927","mc":"江天化学","jys":"sz"},{"dm":"603216","mc":"梦天家居","jys":"sh"},{"dm":"600963","mc":"岳阳林纸","jys":"sh"},{"dm":"600623","mc":"华谊集团","jys":"sh"},{"dm":"603153","mc":"上海建科","jys":"sh"},{"dm":"688386","mc":"泛亚微透","jys":"sh"},{"dm":"601616","mc":"广电电气","jys":"sh"},{"dm":"600257","mc":"大湖股份","jys":"sh"},{"dm":"000922","mc":"佳电股份","jys":"sz"},{"dm":"603070","mc":"万控智造","jys":"sh"},{"dm":"300962","mc":"中金辐照","jys":"sz"},{"dm":"000722","mc":"湖南发展","jys":"sz"},{"dm":"603706","mc":"东方环宇","jys":"sh"},{"dm":"600339","mc":"中油工程","jys":"sh"},{"dm":"600818","mc":"中路股份","jys":"sh"},{"dm":"600438","mc":"通威股份","jys":"sh"},{"dm":"301127","mc":"天源环保","jys":"sz"},{"dm":"605268","mc":"王力安防","jys":"sh"},{"dm":"600979","mc":"广安爱众","jys":"sh"},{"dm":"002388","mc":"新亚制程","jys":"sz"},{"dm":"300626","mc":"华瑞股份","jys":"sz"},{"dm":"600420","mc":"国药现代","jys":"sh"},{"dm":"600343","mc":"航天动力","jys":"sh"},{"dm":"300096","mc":"易联众","jys":"sz"},{"dm":"000531","mc":"穗恒运A","jys":"sz"},{"dm":"688198","mc":"佰仁医疗","jys":"sh"},{"dm":"688355","mc":"明志科技","jys":"sh"},{"dm":"600705","mc":"中航产融","jys":"sh"},{"dm":"300905","mc":"宝丽迪","jys":"sz"},{"dm":"605287","mc":"德才股份","jys":"sh"},{"dm":"688432","mc":"有研硅","jys":"sh"},{"dm":"300567","mc":"精测电子","jys":"sz"},{"dm":"603065","mc":"宿迁联盛","jys":"sh"},{"dm":"301290","mc":"东星医疗","jys":"sz"},{"dm":"603739","mc":"蔚蓝生物","jys":"sh"},{"dm":"600573","mc":"惠泉啤酒","jys":"sh"},{"dm":"300854","mc":"中兰环保","jys":"sz"},{"dm":"000838","mc":"财信发展","jys":"sz"},{"dm":"301281","mc":"科源制药","jys":"sz"},{"dm":"002066","mc":"瑞泰科技","jys":"sz"},{"dm":"600579","mc":"克劳斯","jys":"sh"},{"dm":"600794","mc":"保税科技","jys":"sh"},{"dm":"000544","mc":"中原环保","jys":"sz"},{"dm":"600392","mc":"盛和资源","jys":"sh"},{"dm":"000863","mc":"三湘印象","jys":"sz"},{"dm":"301212","mc":"联盛化学","jys":"sz"},{"dm":"600570","mc":"恒生电子","jys":"sh"},{"dm":"301180","mc":"万祥科技","jys":"sz"},{"dm":"301061","mc":"匠心家居","jys":"sz"},{"dm":"002982","mc":"湘佳股份","jys":"sz"},{"dm":"300789","mc":"唐源电气","jys":"sz"},{"dm":"300324","mc":"旋极信息","jys":"sz"},{"dm":"301209","mc":"联合化学","jys":"sz"},{"dm":"688563","mc":"航材股份","jys":"sh"},{"dm":"300118","mc":"东方日升","jys":"sz"},{"dm":"605068","mc":"明新旭腾","jys":"sh"},{"dm":"601058","mc":"赛轮轮胎","jys":"sh"},{"dm":"600282","mc":"南钢股份","jys":"sh"},{"dm":"002392","mc":"北京利尔","jys":"sz"},{"dm":"301220","mc":"亚香股份","jys":"sz"},{"dm":"600918","mc":"中泰证券","jys":"sh"},{"dm":"600212","mc":"绿能慧充","jys":"sh"},{"dm":"300259","mc":"新天科技","jys":"sz"},{"dm":"002635","mc":"安洁科技","jys":"sz"},{"dm":"002549","mc":"凯美特气","jys":"sz"},{"dm":"600668","mc":"尖峰集团","jys":"sh"},{"dm":"000069","mc":"华侨城A","jys":"sz"},{"dm":"600120","mc":"浙江东方","jys":"sh"},{"dm":"002908","mc":"德生科技","jys":"sz"},{"dm":"001287","mc":"中电港","jys":"sz"},{"dm":"603255","mc":"鼎际得","jys":"sh"},{"dm":"300739","mc":"明阳电路","jys":"sz"},{"dm":"000750","mc":"国海证券","jys":"sz"},{"dm":"300741","mc":"华宝股份","jys":"sz"},{"dm":"002606","mc":"大连电瓷","jys":"sz"},{"dm":"002925","mc":"盈趣科技","jys":"sz"},{"dm":"300793","mc":"佳禾智能","jys":"sz"},{"dm":"603839","mc":"安正时尚","jys":"sh"},{"dm":"600976","mc":"健民集团","jys":"sh"},{"dm":"603324","mc":"盛剑环境","jys":"sh"},{"dm":"600080","mc":"金花股份","jys":"sh"},{"dm":"000633","mc":"合金投资","jys":"sz"},{"dm":"603051","mc":"鹿山新材","jys":"sh"},{"dm":"002346","mc":"柘中股份","jys":"sz"},{"dm":"600127","mc":"金健米业","jys":"sh"},{"dm":"300205","mc":"天喻信息","jys":"sz"},{"dm":"002813","mc":"路畅科技","jys":"sz"},{"dm":"000686","mc":"东北证券","jys":"sz"},{"dm":"300999","mc":"金龙鱼","jys":"sz"},{"dm":"300562","mc":"乐心医疗","jys":"sz"},{"dm":"603191","mc":"望变电气","jys":"sh"},{"dm":"601009","mc":"南京银行","jys":"sh"},{"dm":"300253","mc":"卫宁健康","jys":"sz"},{"dm":"600860","mc":"京城股份","jys":"sh"},{"dm":"301448","mc":"开创电气","jys":"sz"},{"dm":"002318","mc":"久立特材","jys":"sz"},{"dm":"600495","mc":"晋西车轴","jys":"sh"},{"dm":"001270","mc":"铖昌科技","jys":"sz"},{"dm":"002759","mc":"天际股份","jys":"sz"},{"dm":"003026","mc":"中晶科技","jys":"sz"},{"dm":"003000","mc":"劲仔食品","jys":"sz"},{"dm":"000923","mc":"河钢资源","jys":"sz"},{"dm":"002724","mc":"海洋王","jys":"sz"},{"dm":"000099","mc":"中信海直","jys":"sz"},{"dm":"601515","mc":"东风股份","jys":"sh"},{"dm":"600879","mc":"航天电子","jys":"sh"},{"dm":"000652","mc":"泰达股份","jys":"sz"},{"dm":"301345","mc":"涛涛车业","jys":"sz"},{"dm":"600836","mc":"上海易连","jys":"sh"},{"dm":"001212","mc":"中旗新材","jys":"sz"},{"dm":"601990","mc":"南京证券","jys":"sh"},{"dm":"603173","mc":"福斯达","jys":"sh"},{"dm":"301270","mc":"汉仪股份","jys":"sz"},{"dm":"002878","mc":"元隆雅图","jys":"sz"},{"dm":"300484","mc":"蓝海华腾","jys":"sz"},{"dm":"000402","mc":"金 融 街","jys":"sz"},{"dm":"300681","mc":"英搏尔","jys":"sz"},{"dm":"002650","mc":"加加食品","jys":"sz"},{"dm":"300831","mc":"派瑞股份","jys":"sz"},{"dm":"603113","mc":"金能科技","jys":"sh"},{"dm":"603933","mc":"睿能科技","jys":"sh"},{"dm":"002399","mc":"海普瑞","jys":"sz"},{"dm":"600095","mc":"湘财股份","jys":"sh"},{"dm":"300491","mc":"通合科技","jys":"sz"},{"dm":"688216","mc":"气派科技","jys":"sh"},{"dm":"600608","mc":"ST沪科","jys":"sh"},{"dm":"600982","mc":"宁波能源","jys":"sh"},{"dm":"301390","mc":"经纬股份","jys":"sz"},{"dm":"600452","mc":"涪陵电力","jys":"sh"},{"dm":"300838","mc":"浙江力诺","jys":"sz"},{"dm":"000020","mc":"深华发A","jys":"sz"},{"dm":"002227","mc":"奥 特 迅","jys":"sz"},{"dm":"000584","mc":"ST工智","jys":"sz"},{"dm":"688307","mc":"中润光学","jys":"sh"},{"dm":"688293","mc":"奥浦迈","jys":"sh"},{"dm":"000632","mc":"三木集团","jys":"sz"},{"dm":"300053","mc":"航宇微","jys":"sz"},{"dm":"688488","mc":"艾迪药业","jys":"sh"},{"dm":"300963","mc":"中洲特材","jys":"sz"},{"dm":"301078","mc":"孩子王","jys":"sz"},{"dm":"000514","mc":"渝 开 发","jys":"sz"},{"dm":"002334","mc":"英威腾","jys":"sz"},{"dm":"603810","mc":"丰山集团","jys":"sh"},{"dm":"600618","mc":"氯碱化工","jys":"sh"},{"dm":"002942","mc":"新农股份","jys":"sz"},{"dm":"300564","mc":"筑博设计","jys":"sz"},{"dm":"601022","mc":"宁波远洋","jys":"sh"},{"dm":"301198","mc":"喜悦智行","jys":"sz"},{"dm":"003008","mc":"开普检测","jys":"sz"},{"dm":"603568","mc":"伟明环保","jys":"sh"},{"dm":"600243","mc":"青海华鼎","jys":"sh"},{"dm":"301023","mc":"江南奕帆","jys":"sz"},{"dm":"300834","mc":"星辉环材","jys":"sz"},{"dm":"300960","mc":"通业科技","jys":"sz"},{"dm":"603282","mc":"亚光股份","jys":"sh"},{"dm":"600163","mc":"中闽能源","jys":"sh"},{"dm":"000755","mc":"山西路桥","jys":"sz"},{"dm":"000526","mc":"学大教育","jys":"sz"},{"dm":"688107","mc":"安路科技","jys":"sh"},{"dm":"300121","mc":"阳谷华泰","jys":"sz"},{"dm":"600663","mc":"陆家嘴","jys":"sh"},{"dm":"600650","mc":"锦江在线","jys":"sh"},{"dm":"603663","mc":"三祥新材","jys":"sh"},{"dm":"600052","mc":"东望时代","jys":"sh"},{"dm":"601398","mc":"工商银行","jys":"sh"},{"dm":"600315","mc":"上海家化","jys":"sh"},{"dm":"605339","mc":"南侨食品","jys":"sh"},{"dm":"688651","mc":"盛邦安全","jys":"sh"},{"dm":"002522","mc":"浙江众成","jys":"sz"},{"dm":"688093","mc":"世华科技","jys":"sh"},{"dm":"601068","mc":"中铝国际","jys":"sh"},{"dm":"601002","mc":"晋亿实业","jys":"sh"},{"dm":"300022","mc":"吉峰科技","jys":"sz"},{"dm":"301092","mc":"争光股份","jys":"sz"},{"dm":"301089","mc":"拓新药业","jys":"sz"},{"dm":"301040","mc":"中环海陆","jys":"sz"},{"dm":"002226","mc":"江南化工","jys":"sz"},{"dm":"300142","mc":"沃森生物","jys":"sz"},{"dm":"002737","mc":"葵花药业","jys":"sz"},{"dm":"600276","mc":"恒瑞医药","jys":"sh"},{"dm":"002145","mc":"中核钛白","jys":"sz"},{"dm":"603826","mc":"坤彩科技","jys":"sh"},{"dm":"600207","mc":"安彩高科","jys":"sh"},{"dm":"603895","mc":"天永智能","jys":"sh"},{"dm":"300487","mc":"蓝晓科技","jys":"sz"},{"dm":"300897","mc":"山科智能","jys":"sz"},{"dm":"300923","mc":"研奥股份","jys":"sz"},{"dm":"002546","mc":"新联电子","jys":"sz"},{"dm":"600535","mc":"天士力","jys":"sh"},{"dm":"000929","mc":"兰州黄河","jys":"sz"},{"dm":"603029","mc":"天鹅股份","jys":"sh"},{"dm":"001255","mc":"博菲电气","jys":"sz"},{"dm":"601816","mc":"京沪高铁","jys":"sh"},{"dm":"301055","mc":"张小泉","jys":"sz"},{"dm":"603063","mc":"禾望电气","jys":"sh"},{"dm":"002918","mc":"蒙娜丽莎","jys":"sz"},{"dm":"603721","mc":"中广天择","jys":"sh"},{"dm":"002139","mc":"拓邦股份","jys":"sz"},{"dm":"603336","mc":"宏辉果蔬","jys":"sh"},{"dm":"301189","mc":"奥尼电子","jys":"sz"},{"dm":"601568","mc":"北元集团","jys":"sh"},{"dm":"002872","mc":"ST天圣","jys":"sz"},{"dm":"000786","mc":"北新建材","jys":"sz"},{"dm":"605222","mc":"起帆电缆","jys":"sh"},{"dm":"301035","mc":"润丰股份","jys":"sz"},{"dm":"300254","mc":"仟源医药","jys":"sz"},{"dm":"002255","mc":"海陆重工","jys":"sz"},{"dm":"300174","mc":"元力股份","jys":"sz"},{"dm":"002494","mc":"华斯股份","jys":"sz"},{"dm":"300317","mc":"珈伟新能","jys":"sz"},{"dm":"300285","mc":"国瓷材料","jys":"sz"},{"dm":"603291","mc":"联合水务","jys":"sh"},{"dm":"603103","mc":"横店影视","jys":"sh"},{"dm":"300164","mc":"通源石油","jys":"sz"},{"dm":"000719","mc":"中原传媒","jys":"sz"},{"dm":"600802","mc":"福建水泥","jys":"sh"},{"dm":"300389","mc":"艾比森","jys":"sz"},{"dm":"600506","mc":"统一股份","jys":"sh"},{"dm":"300839","mc":"博汇股份","jys":"sz"},{"dm":"605178","mc":"时空科技","jys":"sh"},{"dm":"688163","mc":"赛伦生物","jys":"sh"},{"dm":"603348","mc":"文灿股份","jys":"sh"},{"dm":"603097","mc":"江苏华辰","jys":"sh"},{"dm":"002046","mc":"国机精工","jys":"sz"},{"dm":"000301","mc":"东方盛虹","jys":"sz"},{"dm":"301161","mc":"唯万密封","jys":"sz"},{"dm":"300788","mc":"中信出版","jys":"sz"},{"dm":"603214","mc":"爱婴室","jys":"sh"},{"dm":"300536","mc":"农尚环境","jys":"sz"},{"dm":"301175","mc":"中科环保","jys":"sz"},{"dm":"600525","mc":"长园集团","jys":"sh"},{"dm":"601998","mc":"中信银行","jys":"sh"},{"dm":"600149","mc":"廊坊发展","jys":"sh"},{"dm":"301418","mc":"协昌科技","jys":"sz"},{"dm":"002012","mc":"凯恩股份","jys":"sz"},{"dm":"600887","mc":"伊利股份","jys":"sh"},{"dm":"002059","mc":"云南旅游","jys":"sz"},{"dm":"000859","mc":"国风新材","jys":"sz"},{"dm":"003035","mc":"南网能源","jys":"sz"},{"dm":"002678","mc":"珠江钢琴","jys":"sz"},{"dm":"688081","mc":"兴图新科","jys":"sh"},{"dm":"688575","mc":"亚辉龙","jys":"sh"},{"dm":"600925","mc":"苏能股份","jys":"sh"},{"dm":"002121","mc":"科陆电子","jys":"sz"},{"dm":"603690","mc":"至纯科技","jys":"sh"},{"dm":"603181","mc":"皇马科技","jys":"sh"},{"dm":"300284","mc":"苏交科","jys":"sz"},{"dm":"000530","mc":"冰山冷热","jys":"sz"},{"dm":"301255","mc":"通力科技","jys":"sz"},{"dm":"300995","mc":"奇德新材","jys":"sz"},{"dm":"003003","mc":"天元股份","jys":"sz"},{"dm":"300674","mc":"宇信科技","jys":"sz"},{"dm":"300046","mc":"台基股份","jys":"sz"},{"dm":"000902","mc":"新洋丰","jys":"sz"},{"dm":"688334","mc":"西高院","jys":"sh"},{"dm":"600543","mc":"*ST莫高","jys":"sh"},{"dm":"301469","mc":"恒达新材","jys":"sz"},{"dm":"300573","mc":"兴齐眼药","jys":"sz"},{"dm":"300354","mc":"东华测试","jys":"sz"},{"dm":"603226","mc":"菲林格尔","jys":"sh"},{"dm":"688529","mc":"豪森股份","jys":"sh"},{"dm":"301158","mc":"德石股份","jys":"sz"},{"dm":"000504","mc":"南华生物","jys":"sz"},{"dm":"300134","mc":"大富科技","jys":"sz"},{"dm":"002880","mc":"卫光生物","jys":"sz"},{"dm":"300950","mc":"德固特","jys":"sz"},{"dm":"603856","mc":"东宏股份","jys":"sh"},{"dm":"605303","mc":"园林股份","jys":"sh"},{"dm":"000059","mc":"华锦股份","jys":"sz"},{"dm":"603272","mc":"联翔股份","jys":"sh"},{"dm":"300137","mc":"先河环保","jys":"sz"},{"dm":"000565","mc":"渝三峡A","jys":"sz"},{"dm":"600455","mc":"博通股份","jys":"sh"},{"dm":"002469","mc":"三维化学","jys":"sz"},{"dm":"002911","mc":"佛燃能源","jys":"sz"},{"dm":"600168","mc":"武汉控股","jys":"sh"},{"dm":"600056","mc":"中国医药","jys":"sh"},{"dm":"001300","mc":"三柏硕","jys":"sz"},{"dm":"002333","mc":"罗普斯金","jys":"sz"},{"dm":"603389","mc":"亚振家居","jys":"sh"},{"dm":"003027","mc":"同兴环保","jys":"sz"},{"dm":"002097","mc":"山河智能","jys":"sz"},{"dm":"300289","mc":"利德曼","jys":"sz"},{"dm":"300054","mc":"鼎龙股份","jys":"sz"},{"dm":"603727","mc":"博迈科","jys":"sh"},{"dm":"605305","mc":"中际联合","jys":"sh"},{"dm":"002404","mc":"嘉欣丝绸","jys":"sz"},{"dm":"300971","mc":"博亚精工","jys":"sz"},{"dm":"002987","mc":"京北方","jys":"sz"},{"dm":"603105","mc":"芯能科技","jys":"sh"},{"dm":"002696","mc":"百洋股份","jys":"sz"},{"dm":"600965","mc":"福成股份","jys":"sh"},{"dm":"300062","mc":"中能电气","jys":"sz"},{"dm":"000937","mc":"冀中能源","jys":"sz"},{"dm":"603900","mc":"莱绅通灵","jys":"sh"},{"dm":"002567","mc":"唐人神","jys":"sz"},{"dm":"300859","mc":"*ST西域","jys":"sz"},{"dm":"300385","mc":"雪浪环境","jys":"sz"},{"dm":"300510","mc":"金冠股份","jys":"sz"},{"dm":"002558","mc":"巨人网络","jys":"sz"},{"dm":"002946","mc":"新乳业","jys":"sz"},{"dm":"002065","mc":"东华软件","jys":"sz"},{"dm":"300699","mc":"光威复材","jys":"sz"},{"dm":"300635","mc":"中达安","jys":"sz"},{"dm":"002391","mc":"长青股份","jys":"sz"},{"dm":"300376","mc":"易事特","jys":"sz"},{"dm":"300374","mc":"中铁装配","jys":"sz"},{"dm":"688458","mc":"美芯晟","jys":"sh"},{"dm":"301043","mc":"绿岛风","jys":"sz"},{"dm":"603656","mc":"泰禾智能","jys":"sh"},{"dm":"002860","mc":"星帅尔","jys":"sz"},{"dm":"603356","mc":"华菱精工","jys":"sh"},{"dm":"001318","mc":"阳光乳业","jys":"sz"},{"dm":"688207","mc":"格灵深瞳","jys":"sh"},{"dm":"002444","mc":"巨星科技","jys":"sz"},{"dm":"600644","mc":"乐山电力","jys":"sh"},{"dm":"300307","mc":"慈星股份","jys":"sz"},{"dm":"601609","mc":"金田股份","jys":"sh"},{"dm":"300609","mc":"汇纳科技","jys":"sz"},{"dm":"600268","mc":"国电南自","jys":"sh"},{"dm":"002448","mc":"中原内配","jys":"sz"},{"dm":"301066","mc":"万事利","jys":"sz"},{"dm":"002927","mc":"泰永长征","jys":"sz"},{"dm":"001373","mc":"翔腾新材","jys":"sz"},{"dm":"601006","mc":"大秦铁路","jys":"sh"},{"dm":"002985","mc":"北摩高科","jys":"sz"},{"dm":"300126","mc":"锐奇股份","jys":"sz"},{"dm":"002100","mc":"天康生物","jys":"sz"},{"dm":"301166","mc":"优宁维","jys":"sz"},{"dm":"603396","mc":"金辰股份","jys":"sh"},{"dm":"002169","mc":"智光电气","jys":"sz"},{"dm":"600674","mc":"川投能源","jys":"sh"},{"dm":"600814","mc":"杭州解百","jys":"sh"},{"dm":"600111","mc":"北方稀土","jys":"sh"},{"dm":"300823","mc":"建科机械","jys":"sz"},{"dm":"600998","mc":"九州通","jys":"sh"},{"dm":"300419","mc":"浩丰科技","jys":"sz"},{"dm":"002130","mc":"沃尔核材","jys":"sz"},{"dm":"002741","mc":"光华科技","jys":"sz"},{"dm":"301232","mc":"飞沃科技","jys":"sz"},{"dm":"002171","mc":"楚江新材","jys":"sz"},{"dm":"000731","mc":"四川美丰","jys":"sz"},{"dm":"600182","mc":"S佳通","jys":"sh"},{"dm":"002693","mc":"双成药业","jys":"sz"},{"dm":"600900","mc":"长江电力","jys":"sh"},{"dm":"000831","mc":"中国稀土","jys":"sz"},{"dm":"688146","mc":"中船特气","jys":"sh"},{"dm":"688389","mc":"普门科技","jys":"sh"},{"dm":"300387","mc":"富邦股份","jys":"sz"},{"dm":"000707","mc":"双环科技","jys":"sz"},{"dm":"603444","mc":"吉比特","jys":"sh"},{"dm":"603685","mc":"晨丰科技","jys":"sh"},{"dm":"300550","mc":"和仁科技","jys":"sz"},{"dm":"300795","mc":"米奥会展","jys":"sz"},{"dm":"001333","mc":"光华股份","jys":"sz"},{"dm":"688681","mc":"科汇股份","jys":"sh"},{"dm":"688192","mc":"迪哲医药-U","jys":"sh"},{"dm":"300270","mc":"中威电子","jys":"sz"},{"dm":"002297","mc":"博云新材","jys":"sz"},{"dm":"605122","mc":"四方新材","jys":"sh"},{"dm":"001872","mc":"招商港口","jys":"sz"},{"dm":"688353","mc":"华盛锂电","jys":"sh"},{"dm":"002951","mc":"ST金时","jys":"sz"},{"dm":"002414","mc":"高德红外","jys":"sz"},{"dm":"605168","mc":"三人行","jys":"sh"},{"dm":"002891","mc":"中宠股份","jys":"sz"},{"dm":"605499","mc":"东鹏饮料","jys":"sh"},{"dm":"600980","mc":"北矿科技","jys":"sh"},{"dm":"601369","mc":"陕鼓动力","jys":"sh"},{"dm":"600927","mc":"永安期货","jys":"sh"},{"dm":"300512","mc":"中亚股份","jys":"sz"},{"dm":"301323","mc":"新莱福","jys":"sz"},{"dm":"600883","mc":"博闻科技","jys":"sh"},{"dm":"600228","mc":"返利科技","jys":"sh"},{"dm":"603110","mc":"东方材料","jys":"sh"},{"dm":"301003","mc":"江苏博云","jys":"sz"},{"dm":"002543","mc":"万和电气","jys":"sz"},{"dm":"002101","mc":"广东鸿图","jys":"sz"},{"dm":"600273","mc":"嘉化能源","jys":"sh"},{"dm":"000848","mc":"承德露露","jys":"sz"},{"dm":"603201","mc":"常润股份","jys":"sh"},{"dm":"301097","mc":"天益医疗","jys":"sz"},{"dm":"600483","mc":"福能股份","jys":"sh"},{"dm":"002403","mc":"爱仕达","jys":"sz"},{"dm":"603871","mc":"嘉友国际","jys":"sh"},{"dm":"600721","mc":"百花医药","jys":"sh"},{"dm":"000547","mc":"航天发展","jys":"sz"},{"dm":"001209","mc":"洪兴股份","jys":"sz"},{"dm":"603689","mc":"皖天然气","jys":"sh"},{"dm":"002842","mc":"翔鹭钨业","jys":"sz"},{"dm":"300806","mc":"斯迪克","jys":"sz"},{"dm":"688283","mc":"坤恒顺维","jys":"sh"},{"dm":"603275","mc":"众辰科技","jys":"sh"},{"dm":"603609","mc":"禾丰股份","jys":"sh"},{"dm":"000663","mc":"永安林业","jys":"sz"},{"dm":"603338","mc":"浙江鼎力","jys":"sh"},{"dm":"002561","mc":"徐家汇","jys":"sz"},{"dm":"002394","mc":"联发股份","jys":"sz"},{"dm":"301363","mc":"美好医疗","jys":"sz"},{"dm":"603020","mc":"爱普股份","jys":"sh"},{"dm":"600463","mc":"空港股份","jys":"sh"},{"dm":"603565","mc":"中谷物流","jys":"sh"},{"dm":"002026","mc":"山东威达","jys":"sz"},{"dm":"301316","mc":"慧博云通","jys":"sz"},{"dm":"603193","mc":"润本股份","jys":"sh"},{"dm":"688478","mc":"晶升股份","jys":"sh"},{"dm":"300749","mc":"顶固集创","jys":"sz"},{"dm":"301201","mc":"诚达药业","jys":"sz"},{"dm":"000423","mc":"东阿阿胶","jys":"sz"},{"dm":"002745","mc":"木林森","jys":"sz"},{"dm":"603680","mc":"今创集团","jys":"sh"},{"dm":"301507","mc":"民生健康","jys":"sz"},{"dm":"300639","mc":"凯普生物","jys":"sz"},{"dm":"688096","mc":"京源环保","jys":"sh"},{"dm":"603967","mc":"中创物流","jys":"sh"},{"dm":"002736","mc":"国信证券","jys":"sz"},{"dm":"603073","mc":"彩蝶实业","jys":"sh"},{"dm":"688543","mc":"国科军工","jys":"sh"},{"dm":"300052","mc":"中青宝","jys":"sz"},{"dm":"688030","mc":"山石网科","jys":"sh"},{"dm":"002670","mc":"国盛金控","jys":"sz"},{"dm":"688061","mc":"灿瑞科技","jys":"sh"},{"dm":"688685","mc":"迈信林","jys":"sh"},{"dm":"600620","mc":"天宸股份","jys":"sh"},{"dm":"603125","mc":"常青科技","jys":"sh"},{"dm":"002205","mc":"国统股份","jys":"sz"},{"dm":"603313","mc":"梦百合","jys":"sh"},{"dm":"002096","mc":"易普力","jys":"sz"},{"dm":"688287","mc":"观典防务","jys":"sh"},{"dm":"600312","mc":"平高电气","jys":"sh"},{"dm":"600764","mc":"中国海防","jys":"sh"},{"dm":"002731","mc":"萃华珠宝","jys":"sz"},{"dm":"003037","mc":"三和管桩","jys":"sz"},{"dm":"300929","mc":"华骐环保","jys":"sz"},{"dm":"688171","mc":"纬德信息","jys":"sh"},{"dm":"301217","mc":"铜冠铜箔","jys":"sz"},{"dm":"603489","mc":"八方股份","jys":"sh"},{"dm":"688602","mc":"康鹏科技","jys":"sh"},{"dm":"688006","mc":"杭可科技","jys":"sh"},{"dm":"300349","mc":"金卡智能","jys":"sz"},{"dm":"002553","mc":"南方精工","jys":"sz"},{"dm":"688352","mc":"颀中科技","jys":"sh"},{"dm":"300138","mc":"晨光生物","jys":"sz"},{"dm":"300068","mc":"南都电源","jys":"sz"},{"dm":"603365","mc":"水星家纺","jys":"sh"},{"dm":"300943","mc":"春晖智控","jys":"sz"},{"dm":"688277","mc":"天智航-U","jys":"sh"},{"dm":"301029","mc":"怡合达","jys":"sz"},{"dm":"300357","mc":"我武生物","jys":"sz"},{"dm":"603269","mc":"海鸥股份","jys":"sh"},{"dm":"603585","mc":"苏利股份","jys":"sh"},{"dm":"000681","mc":"视觉中国","jys":"sz"},{"dm":"603829","mc":"洛凯股份","jys":"sh"},{"dm":"301365","mc":"矩阵股份","jys":"sz"},{"dm":"002088","mc":"鲁阳节能","jys":"sz"},{"dm":"002552","mc":"宝鼎科技","jys":"sz"},{"dm":"300188","mc":"美亚柏科","jys":"sz"},{"dm":"603045","mc":"福达合金","jys":"sh"},{"dm":"605155","mc":"西大门","jys":"sh"},{"dm":"300804","mc":"广康生化","jys":"sz"},{"dm":"600171","mc":"上海贝岭","jys":"sh"},{"dm":"300628","mc":"亿联网络","jys":"sz"},{"dm":"301278","mc":"快可电子","jys":"sz"},{"dm":"600588","mc":"用友网络","jys":"sh"},{"dm":"300717","mc":"华信新材","jys":"sz"},{"dm":"300922","mc":"天秦装备","jys":"sz"},{"dm":"000997","mc":"新 大 陆","jys":"sz"},{"dm":"605365","mc":"立达信","jys":"sh"},{"dm":"600262","mc":"北方股份","jys":"sh"},{"dm":"300850","mc":"新强联","jys":"sz"},{"dm":"301263","mc":"泰恩康","jys":"sz"},{"dm":"688070","mc":"纵横股份","jys":"sh"},{"dm":"688678","mc":"福立旺","jys":"sh"},{"dm":"300109","mc":"新开源","jys":"sz"},{"dm":"301038","mc":"深水规院","jys":"sz"},{"dm":"603090","mc":"宏盛股份","jys":"sh"},{"dm":"605296","mc":"神农集团","jys":"sh"},{"dm":"301077","mc":"星华新材","jys":"sz"},{"dm":"688499","mc":"利元亨","jys":"sh"},{"dm":"688190","mc":"云路股份","jys":"sh"},{"dm":"300540","mc":"蜀道装备","jys":"sz"},{"dm":"603355","mc":"莱克电气","jys":"sh"},{"dm":"300779","mc":"惠城环保","jys":"sz"},{"dm":"301049","mc":"超越科技","jys":"sz"},{"dm":"002653","mc":"海思科","jys":"sz"},{"dm":"300900","mc":"广联航空","jys":"sz"},{"dm":"301182","mc":"凯旺科技","jys":"sz"},{"dm":"688370","mc":"丛麟科技","jys":"sh"},{"dm":"688255","mc":"凯尔达","jys":"sh"},{"dm":"688091","mc":"上海谊众","jys":"sh"},{"dm":"300803","mc":"指南针","jys":"sz"},{"dm":"001328","mc":"登康口腔","jys":"sz"},{"dm":"300938","mc":"信测标准","jys":"sz"},{"dm":"300033","mc":"同花顺","jys":"sz"},{"dm":"688559","mc":"海目星","jys":"sh"},{"dm":"688598","mc":"金博股份","jys":"sh"},{"dm":"688202","mc":"美迪西","jys":"sh"},{"dm":"688185","mc":"康希诺","jys":"sh"},{"dm":"688718","mc":"唯赛勃","jys":"sh"},{"dm":"688686","mc":"奥普特","jys":"sh"},{"dm":"688653","mc":"康希通信","jys":"sh"},{"dm":"688648","mc":"中邮科技","jys":"sh"},{"dm":"688638","mc":"誉辰智能","jys":"sh"},{"dm":"688585","mc":"上纬新材","jys":"sh"},{"dm":"688561","mc":"奇安信-U","jys":"sh"},{"dm":"688555","mc":"退市泽达","jys":"sh"},{"dm":"688505","mc":"复旦张江","jys":"sh"},{"dm":"688425","mc":"铁建重工","jys":"sh"},{"dm":"688119","mc":"中钢洛耐","jys":"sh"},{"dm":"688098","mc":"申联生物","jys":"sh"},{"dm":"688086","mc":"退市紫晶","jys":"sh"},{"dm":"688057","mc":"金达莱","jys":"sh"},{"dm":"605158","mc":"华达新材","jys":"sh"},{"dm":"603996","mc":"退市中新","jys":"sh"},{"dm":"603896","mc":"寿仙谷","jys":"sh"},{"dm":"603818","mc":"曲美家居","jys":"sh"},{"dm":"603816","mc":"顾家家居","jys":"sh"},{"dm":"603797","mc":"联泰环保","jys":"sh"},{"dm":"603637","mc":"镇海股份","jys":"sh"},{"dm":"603602","mc":"纵横通信","jys":"sh"},{"dm":"603577","mc":"汇金通","jys":"sh"},{"dm":"603558","mc":"健盛集团","jys":"sh"},{"dm":"603557","mc":"ST起步","jys":"sh"},{"dm":"603527","mc":"众源新材","jys":"sh"},{"dm":"603488","mc":"展鹏科技","jys":"sh"},{"dm":"603377","mc":"东方时尚","jys":"sh"},{"dm":"603361","mc":"浙江国祥","jys":"sh"},{"dm":"603359","mc":"东珠生态","jys":"sh"},{"dm":"603357","mc":"设计总院","jys":"sh"},{"dm":"603328","mc":"依顿电子","jys":"sh"},{"dm":"603326","mc":"我乐家居","jys":"sh"},{"dm":"603318","mc":"水发燃气","jys":"sh"},{"dm":"603212","mc":"赛伍技术","jys":"sh"},{"dm":"603169","mc":"兰石重装","jys":"sh"},{"dm":"603157","mc":"退市拉夏","jys":"sh"},{"dm":"603131","mc":"上海沪工","jys":"sh"},{"dm":"603122","mc":"合富中国","jys":"sh"},{"dm":"603067","mc":"振华股份","jys":"sh"},{"dm":"603025","mc":"大豪科技","jys":"sh"},{"dm":"603017","mc":"中衡设计","jys":"sh"},{"dm":"603012","mc":"创力集团","jys":"sh"},{"dm":"603011","mc":"合锻智能","jys":"sh"},{"dm":"601916","mc":"浙商银行","jys":"sh"},{"dm":"601890","mc":"亚星锚链","jys":"sh"},{"dm":"601880","mc":"辽港股份","jys":"sh"},{"dm":"601857","mc":"中国石油","jys":"sh"},{"dm":"601838","mc":"成都银行","jys":"sh"},{"dm":"601808","mc":"中海油服","jys":"sh"},{"dm":"601619","mc":"嘉泽新能","jys":"sh"},{"dm":"601606","mc":"长城军工","jys":"sh"},{"dm":"601588","mc":"北辰实业","jys":"sh"},{"dm":"601558","mc":"退市锐电","jys":"sh"},{"dm":"601518","mc":"吉林高速","jys":"sh"},{"dm":"601388","mc":"怡球资源","jys":"sh"},{"dm":"601368","mc":"绿城水务","jys":"sh"},{"dm":"601326","mc":"秦港股份","jys":"sh"},{"dm":"601299","mc":"中国北车","jys":"sh"},{"dm":"601268","mc":"*ST二重","jys":"sh"},{"dm":"601258","mc":"*ST庞大","jys":"sh"},{"dm":"601238","mc":"广汽集团","jys":"sh"},{"dm":"601229","mc":"上海银行","jys":"sh"},{"dm":"601216","mc":"君正集团","jys":"sh"},{"dm":"601199","mc":"江南水务","jys":"sh"},{"dm":"601169","mc":"北京银行","jys":"sh"},{"dm":"601158","mc":"重庆水务","jys":"sh"},{"dm":"601121","mc":"宝地矿业","jys":"sh"},{"dm":"601108","mc":"财通证券","jys":"sh"},{"dm":"601107","mc":"四川成渝","jys":"sh"},{"dm":"601106","mc":"中国一重","jys":"sh"},{"dm":"601069","mc":"西部黄金","jys":"sh"},{"dm":"601015","mc":"陕西黑猫","jys":"sh"},{"dm":"601008","mc":"连云港","jys":"sh"},{"dm":"600999","mc":"招商证券","jys":"sh"},{"dm":"600991","mc":"广汽长丰","jys":"sh"},{"dm":"600984","mc":"建设机械","jys":"sh"},{"dm":"600978","mc":"*ST宜生","jys":"sh"},{"dm":"600959","mc":"江苏有线","jys":"sh"},{"dm":"600929","mc":"雪天盐业","jys":"sh"},{"dm":"600908","mc":"无锡银行","jys":"sh"},{"dm":"600899","mc":"*ST信联","jys":"sh"},{"dm":"600898","mc":"ST美讯","jys":"sh"},{"dm":"600896","mc":"退市海医","jys":"sh"},{"dm":"600891","mc":"退市秋林","jys":"sh"},{"dm":"600890","mc":"退市中房","jys":"sh"},{"dm":"600880","mc":"博瑞传播","jys":"sh"},{"dm":"600878","mc":"*ST北科","jys":"sh"},{"dm":"600873","mc":"梅花生物","jys":"sh"},{"dm":"600870","mc":"退市厦华","jys":"sh"},{"dm":"600868","mc":"梅雁吉祥","jys":"sh"},{"dm":"600856","mc":"退市中天","jys":"sh"},{"dm":"600852","mc":"*ST中川","jys":"sh"},{"dm":"600849","mc":"上药转换","jys":"sh"},{"dm":"600848","mc":"上海临港","jys":"sh"},{"dm":"600842","mc":"中西药业","jys":"sh"},{"dm":"600840","mc":"新湖创业","jys":"sh"},{"dm":"600832","mc":"东方明珠","jys":"sh"},{"dm":"600824","mc":"益民集团","jys":"sh"},{"dm":"600823","mc":"ST世茂","jys":"sh"},{"dm":"600820","mc":"隧道股份","jys":"sh"},{"dm":"600815","mc":"厦工股份","jys":"sh"},{"dm":"600813","mc":"ST鞍一工","jys":"sh"},{"dm":"600806","mc":"退市昆机","jys":"sh"},{"dm":"600800","mc":"渤海化学","jys":"sh"},{"dm":"600799","mc":"*ST龙科","jys":"sh"},{"dm":"600798","mc":"宁波海运","jys":"sh"},{"dm":"600795","mc":"国电电力","jys":"sh"},{"dm":"600789","mc":"鲁抗医药","jys":"sh"},{"dm":"600788","mc":"*ST达曼","jys":"sh"},{"dm":"600786","mc":"东方锅炉","jys":"sh"},{"dm":"600781","mc":"退市辅仁","jys":"sh"},{"dm":"600772","mc":"S*ST龙昌","jys":"sh"},{"dm":"600767","mc":"退市运盛","jys":"sh"},{"dm":"600762","mc":"S*ST金荔","jys":"sh"},{"dm":"600752","mc":"*ST哈慈","jys":"sh"},{"dm":"600747","mc":"退市大控","jys":"sh"},{"dm":"600739","mc":"辽宁成大","jys":"sh"},{"dm":"600728","mc":"佳都科技","jys":"sh"},{"dm":"600727","mc":"鲁北化工","jys":"sh"},{"dm":"600724","mc":"宁波富达","jys":"sh"},{"dm":"600723","mc":"首商股份","jys":"sh"},{"dm":"600718","mc":"东软集团","jys":"sh"},{"dm":"600715","mc":"文投控股","jys":"sh"},{"dm":"600710","mc":"苏美达","jys":"sh"},{"dm":"600709","mc":"ST生态","jys":"sh"},{"dm":"600708","mc":"光明地产","jys":"sh"},{"dm":"600704","mc":"物产中大","jys":"sh"},{"dm":"600701","mc":"退市工新","jys":"sh"},{"dm":"600700","mc":"*ST数码","jys":"sh"},{"dm":"600695","mc":"退市绿庭","jys":"sh"},{"dm":"600693","mc":"东百集团","jys":"sh"},{"dm":"600691","mc":"阳煤化工","jys":"sh"},{"dm":"600688","mc":"上海石化","jys":"sh"},{"dm":"600687","mc":"退市刚泰","jys":"sh"},{"dm":"600682","mc":"南京新百","jys":"sh"},{"dm":"600681","mc":"百川能源","jys":"sh"},{"dm":"600680","mc":"*ST上普","jys":"sh"},{"dm":"600677","mc":"*ST航通","jys":"sh"},{"dm":"600672","mc":"*ST华圣","jys":"sh"},{"dm":"600670","mc":"*ST斯达","jys":"sh"},{"dm":"600669","mc":"*ST鞍成","jys":"sh"},{"dm":"600664","mc":"哈药股份","jys":"sh"},{"dm":"600659","mc":"*ST花雕","jys":"sh"},{"dm":"600656","mc":"退市博元","jys":"sh"},{"dm":"600652","mc":"退市游久","jys":"sh"},{"dm":"600651","mc":"飞乐音响","jys":"sh"},{"dm":"600648","mc":"外高桥","jys":"sh"},{"dm":"600646","mc":"ST国嘉","jys":"sh"},{"dm":"600634","mc":"退市富控","jys":"sh"},{"dm":"600632","mc":"华联商厦","jys":"sh"},{"dm":"600631","mc":"百联股份","jys":"sh"},{"dm":"600627","mc":"上电股份","jys":"sh"},{"dm":"600625","mc":"PT水仙","jys":"sh"},{"dm":"600624","mc":"复旦复华","jys":"sh"},{"dm":"600622","mc":"光大嘉宝","jys":"sh"},{"dm":"600614","mc":"退市鹏起","jys":"sh"},{"dm":"600607","mc":"上实医药","jys":"sh"},{"dm":"600606","mc":"绿地控股","jys":"sh"},{"dm":"600591","mc":"*ST上航","jys":"sh"},{"dm":"600577","mc":"精达股份","jys":"sh"},{"dm":"600568","mc":"ST中珠","jys":"sh"},{"dm":"600555","mc":"退市海创","jys":"sh"},{"dm":"600553","mc":"太行水泥","jys":"sh"},{"dm":"600550","mc":"保变电气","jys":"sh"},{"dm":"600545","mc":"卓郎智能","jys":"sh"},{"dm":"600539","mc":"狮头股份","jys":"sh"},{"dm":"600537","mc":"亿晶光电","jys":"sh"},{"dm":"600533","mc":"栖霞建设","jys":"sh"},{"dm":"600532","mc":"退市未来","jys":"sh"},{"dm":"600515","mc":"海南机场","jys":"sh"},{"dm":"600510","mc":"黑牡丹","jys":"sh"},{"dm":"600505","mc":"西昌电力","jys":"sh"},{"dm":"600503","mc":"华丽家族","jys":"sh"},{"dm":"600485","mc":"*ST信威","jys":"sh"},{"dm":"600472","mc":"包头铝业","jys":"sh"},{"dm":"600470","mc":"六国化工","jys":"sh"},{"dm":"600469","mc":"风神股份","jys":"sh"},{"dm":"600466","mc":"*ST蓝光","jys":"sh"},{"dm":"600462","mc":"ST九有","jys":"sh"},{"dm":"600458","mc":"时代新材","jys":"sh"},{"dm":"600432","mc":"退市吉恩","jys":"sh"},{"dm":"600403","mc":"大有能源","jys":"sh"},{"dm":"600401","mc":"退市海润","jys":"sh"},{"dm":"600400","mc":"红豆股份","jys":"sh"},{"dm":"600393","mc":"ST粤泰","jys":"sh"},{"dm":"600390","mc":"五矿资本","jys":"sh"},{"dm":"600387","mc":"ST海越","jys":"sh"},{"dm":"600385","mc":"退市金泰","jys":"sh"},{"dm":"600357","mc":"承德钒钛","jys":"sh"},{"dm":"600354","mc":"敦煌种业","jys":"sh"},{"dm":"600321","mc":"正源股份","jys":"sh"},{"dm":"600317","mc":"营口港","jys":"sh"},{"dm":"600311","mc":"*ST荣华","jys":"sh"},{"dm":"600310","mc":"广西能源","jys":"sh"},{"dm":"600307","mc":"酒钢宏兴","jys":"sh"},{"dm":"600296","mc":"S兰铝","jys":"sh"},{"dm":"600292","mc":"远达环保","jys":"sh"},{"dm":"600291","mc":"退市西水","jys":"sh"},{"dm":"600289","mc":"ST信通","jys":"sh"},{"dm":"600287","mc":"江苏舜天","jys":"sh"},{"dm":"600286","mc":"S*ST国瓷","jys":"sh"},{"dm":"600275","mc":"退市昌鱼","jys":"sh"},{"dm":"600270","mc":"外运发展","jys":"sh"},{"dm":"600263","mc":"路桥建设","jys":"sh"},{"dm":"600260","mc":"*ST凯乐","jys":"sh"},{"dm":"600253","mc":"天方药业","jys":"sh"},{"dm":"600247","mc":"*ST成城","jys":"sh"},{"dm":"600242","mc":"退市中昌","jys":"sh"},{"dm":"600240","mc":"退市华业","jys":"sh"},{"dm":"600225","mc":"卓朗科技","jys":"sh"},{"dm":"600209","mc":"退市罗顿","jys":"sh"},{"dm":"600205","mc":"S山东铝","jys":"sh"},{"dm":"600192","mc":"长城电工","jys":"sh"},{"dm":"600191","mc":"华资实业","jys":"sh"},{"dm":"600181","mc":"S*ST云大","jys":"sh"},{"dm":"600177","mc":"雅戈尔","jys":"sh"},{"dm":"600175","mc":"退市美都","jys":"sh"},{"dm":"600172","mc":"黄河旋风","jys":"sh"},{"dm":"600170","mc":"上海建工","jys":"sh"},{"dm":"600162","mc":"香江控股","jys":"sh"},{"dm":"600156","mc":"华升股份","jys":"sh"},{"dm":"600146","mc":"退市环球","jys":"sh"},{"dm":"600145","mc":"退市新亿","jys":"sh"},{"dm":"600139","mc":"*ST西源","jys":"sh"},{"dm":"600122","mc":"*ST宏图","jys":"sh"},{"dm":"600121","mc":"郑州煤电","jys":"sh"},{"dm":"600113","mc":"浙江东日","jys":"sh"},{"dm":"600112","mc":"ST天成","jys":"sh"},{"dm":"600102","mc":"莱钢股份","jys":"sh"},{"dm":"600093","mc":"退市易见","jys":"sh"},{"dm":"600092","mc":"S*ST精密","jys":"sh"},{"dm":"600091","mc":"退市明科","jys":"sh"},{"dm":"600090","mc":"退市济堂","jys":"sh"},{"dm":"600087","mc":"退市长油","jys":"sh"},{"dm":"600086","mc":"退市金钰","jys":"sh"},{"dm":"600082","mc":"海泰发展","jys":"sh"},{"dm":"600077","mc":"*ST宋都","jys":"sh"},{"dm":"600074","mc":"退市保千","jys":"sh"},{"dm":"600069","mc":"退市银鸽","jys":"sh"},{"dm":"600068","mc":"葛洲坝","jys":"sh"},{"dm":"600067","mc":"冠城大通","jys":"sh"},{"dm":"600065","mc":"*ST联谊","jys":"sh"},{"dm":"600050","mc":"中国联通","jys":"sh"},{"dm":"600035","mc":"楚天高速","jys":"sh"},{"dm":"600028","mc":"中国石化","jys":"sh"},{"dm":"600022","mc":"山东钢铁","jys":"sh"},{"dm":"600019","mc":"宝钢股份","jys":"sh"},{"dm":"600018","mc":"上港集团","jys":"sh"},{"dm":"600005","mc":"武钢股份","jys":"sh"},{"dm":"600003","mc":"ST东北高","jys":"sh"},{"dm":"600002","mc":"齐鲁石化","jys":"sh"},{"dm":"600001","mc":"邯郸钢铁","jys":"sh"},{"dm":"301439","mc":"泓淋电力","jys":"sz"},{"dm":"301317","mc":"鑫磊股份","jys":"sz"},{"dm":"301282","mc":"金禄电子","jys":"sz"},{"dm":"301223","mc":"中荣股份","jys":"sz"},{"dm":"301187","mc":"欧圣电气","jys":"sz"},{"dm":"301136","mc":"招标股份","jys":"sz"},{"dm":"301058","mc":"中粮科工","jys":"sz"},{"dm":"301048","mc":"金鹰重工","jys":"sz"},{"dm":"301039","mc":"中集车辆","jys":"sz"},{"dm":"300975","mc":"商络电子","jys":"sz"},{"dm":"300970","mc":"华绿生物","jys":"sz"},{"dm":"300931","mc":"通用电梯","jys":"sz"},{"dm":"300908","mc":"仲景食品","jys":"sz"},{"dm":"300898","mc":"熊猫乳品","jys":"sz"},{"dm":"300891","mc":"惠云钛业","jys":"sz"},{"dm":"300889","mc":"爱克股份","jys":"sz"},{"dm":"300758","mc":"七彩化学","jys":"sz"},{"dm":"300732","mc":"设研院","jys":"sz"},{"dm":"300710","mc":"万隆光电","jys":"sz"},{"dm":"300696","mc":"爱乐达","jys":"sz"},{"dm":"300610","mc":"晨化股份","jys":"sz"},{"dm":"300587","mc":"天铁股份","jys":"sz"},{"dm":"300526","mc":"中潜退","jys":"sz"},{"dm":"300495","mc":"*ST美尚","jys":"sz"},{"dm":"300485","mc":"赛升药业","jys":"sz"},{"dm":"300456","mc":"赛微电子","jys":"sz"},{"dm":"300447","mc":"全信股份","jys":"sz"},{"dm":"300431","mc":"暴风退","jys":"sz"},{"dm":"300392","mc":"腾信退","jys":"sz"},{"dm":"300388","mc":"节能国祯","jys":"sz"},{"dm":"300372","mc":"欣泰退","jys":"sz"},{"dm":"300371","mc":"汇中股份","jys":"sz"},{"dm":"300367","mc":"网力退","jys":"sz"},{"dm":"300362","mc":"天翔退","jys":"sz"},{"dm":"300356","mc":"光一退","jys":"sz"},{"dm":"300343","mc":"联创股份","jys":"sz"},{"dm":"300336","mc":"新文退","jys":"sz"},{"dm":"300330","mc":"计通退","jys":"sz"},{"dm":"300329","mc":"海伦钢琴","jys":"sz"},{"dm":"300325","mc":"德威退","jys":"sz"},{"dm":"300320","mc":"海达股份","jys":"sz"},{"dm":"300312","mc":"邦讯退","jys":"sz"},{"dm":"300310","mc":"宜通世纪","jys":"sz"},{"dm":"300309","mc":"吉艾退","jys":"sz"},{"dm":"300297","mc":"蓝盾退","jys":"sz"},{"dm":"300292","mc":"吴通控股","jys":"sz"},{"dm":"300273","mc":"和佳退","jys":"sz"},{"dm":"300263","mc":"隆华科技","jys":"sz"},{"dm":"300247","mc":"融捷健康","jys":"sz"},{"dm":"300230","mc":"永利股份","jys":"sz"},{"dm":"300223","mc":"北京君正","jys":"sz"},{"dm":"300216","mc":"千山退","jys":"sz"},{"dm":"300209","mc":"ST有棵树","jys":"sz"},{"dm":"300202","mc":"聚龙退","jys":"sz"},{"dm":"300186","mc":"大华农","jys":"sz"},{"dm":"300185","mc":"通裕重工","jys":"sz"},{"dm":"300184","mc":"力源信息","jys":"sz"},{"dm":"300178","mc":"腾邦退","jys":"sz"},{"dm":"300163","mc":"先锋新材","jys":"sz"},{"dm":"300156","mc":"神雾退","jys":"sz"},{"dm":"300155","mc":"安居宝","jys":"sz"},{"dm":"300117","mc":"嘉寓股份","jys":"sz"},{"dm":"300116","mc":"保力新","jys":"sz"},{"dm":"300107","mc":"建新股份","jys":"sz"},{"dm":"300104","mc":"乐视退","jys":"sz"},{"dm":"300090","mc":"盛运退","jys":"sz"},{"dm":"300089","mc":"文化退","jys":"sz"},{"dm":"300078","mc":"思创医惠","jys":"sz"},{"dm":"300064","mc":"金刚退","jys":"sz"},{"dm":"300038","mc":"数知退","jys":"sz"},{"dm":"300028","mc":"金亚退","jys":"sz"},{"dm":"300027","mc":"华谊兄弟","jys":"sz"},{"dm":"300024","mc":"机器人","jys":"sz"},{"dm":"300023","mc":"宝德退","jys":"sz"},{"dm":"300019","mc":"硅宝科技","jys":"sz"},{"dm":"300009","mc":"安科生物","jys":"sz"},{"dm":"003013","mc":"地铁设计","jys":"sz"},{"dm":"002958","mc":"青农商行","jys":"sz"},{"dm":"002936","mc":"郑州银行","jys":"sz"},{"dm":"002933","mc":"新兴装备","jys":"sz"},{"dm":"002839","mc":"张家港行","jys":"sz"},{"dm":"002801","mc":"微光股份","jys":"sz"},{"dm":"002786","mc":"银宝山新","jys":"sz"},{"dm":"002781","mc":"奇信退","jys":"sz"},{"dm":"002770","mc":"科迪退","jys":"sz"},{"dm":"002762","mc":"金发拉比","jys":"sz"},{"dm":"002751","mc":"易尚退","jys":"sz"},{"dm":"002711","mc":"欧浦退","jys":"sz"},{"dm":"002685","mc":"华东重机","jys":"sz"},{"dm":"002684","mc":"猛狮退","jys":"sz"},{"dm":"002680","mc":"长生退","jys":"sz"},{"dm":"002665","mc":"首航高科","jys":"sz"},{"dm":"002659","mc":"凯文教育","jys":"sz"},{"dm":"002656","mc":"ST摩登","jys":"sz"},{"dm":"002641","mc":"公元股份","jys":"sz"},{"dm":"002638","mc":"勤上股份","jys":"sz"},{"dm":"002622","mc":"皓宸医疗","jys":"sz"},{"dm":"002619","mc":"*ST艾格","jys":"sz"},{"dm":"002618","mc":"丹邦退","jys":"sz"},{"dm":"002610","mc":"爱康科技","jys":"sz"},{"dm":"002604","mc":"龙力退","jys":"sz"},{"dm":"002583","mc":"海能达","jys":"sz"},{"dm":"002580","mc":"圣阳股份","jys":"sz"},{"dm":"002578","mc":"闽发铝业","jys":"sz"},{"dm":"002554","mc":"惠博普","jys":"sz"},{"dm":"002540","mc":"亚太科技","jys":"sz"},{"dm":"002539","mc":"云图控股","jys":"sz"},{"dm":"002533","mc":"金杯电工","jys":"sz"},{"dm":"002517","mc":"恺英网络","jys":"sz"},{"dm":"002509","mc":"天茂退","jys":"sz"},{"dm":"002506","mc":"协鑫集成","jys":"sz"},{"dm":"002504","mc":"*ST弘高","jys":"sz"},{"dm":"002503","mc":"*ST搜特","jys":"sz"},{"dm":"002499","mc":"科林退","jys":"sz"},{"dm":"002496","mc":"辉丰股份","jys":"sz"},{"dm":"002486","mc":"嘉麟杰","jys":"sz"},{"dm":"002478","mc":"常宝股份","jys":"sz"},{"dm":"002477","mc":"雏鹰退","jys":"sz"},{"dm":"002473","mc":"圣莱退","jys":"sz"},{"dm":"002464","mc":"众应退","jys":"sz"},{"dm":"002450","mc":"康得退","jys":"sz"},{"dm":"002447","mc":"晨鑫退","jys":"sz"},{"dm":"002443","mc":"金洲管道","jys":"sz"},{"dm":"002439","mc":"启明星辰","jys":"sz"},{"dm":"002427","mc":"尤夫股份","jys":"sz"},{"dm":"002421","mc":"达实智能","jys":"sz"},{"dm":"002417","mc":"深南退","jys":"sz"},{"dm":"002411","mc":"必康退","jys":"sz"},{"dm":"002408","mc":"齐翔腾达","jys":"sz"},{"dm":"002397","mc":"梦洁股份","jys":"sz"},{"dm":"002378","mc":"章源钨业","jys":"sz"},{"dm":"002369","mc":"卓翼科技","jys":"sz"},{"dm":"002361","mc":"神剑股份","jys":"sz"},{"dm":"002359","mc":"北讯退","jys":"sz"},{"dm":"002325","mc":"洪涛股份","jys":"sz"},{"dm":"002321","mc":"华英农业","jys":"sz"},{"dm":"002307","mc":"北新路桥","jys":"sz"},{"dm":"002303","mc":"美盈森","jys":"sz"},{"dm":"002283","mc":"天润工业","jys":"sz"},{"dm":"002260","mc":"德奥退","jys":"sz"},{"dm":"002259","mc":"ST升达","jys":"sz"},{"dm":"002256","mc":"兆新股份","jys":"sz"},{"dm":"002249","mc":"大洋电机","jys":"sz"},{"dm":"002248","mc":"华东数控","jys":"sz"},{"dm":"002246","mc":"北化股份","jys":"sz"},{"dm":"002243","mc":"力合科创","jys":"sz"},{"dm":"002239","mc":"奥特佳","jys":"sz"},{"dm":"002231","mc":"奥维通信","jys":"sz"},{"dm":"002220","mc":"天宝退","jys":"sz"},{"dm":"002214","mc":"大立科技","jys":"sz"},{"dm":"002177","mc":"御银股份","jys":"sz"},{"dm":"002165","mc":"红 宝 丽","jys":"sz"},{"dm":"002163","mc":"海南发展","jys":"sz"},{"dm":"002160","mc":"常铝股份","jys":"sz"},{"dm":"002147","mc":"新光退","jys":"sz"},{"dm":"002143","mc":"印纪退","jys":"sz"},{"dm":"002136","mc":"安 纳 达","jys":"sz"},{"dm":"002135","mc":"东南网架","jys":"sz"},{"dm":"002127","mc":"南极电商","jys":"sz"},{"dm":"002118","mc":"*ST紫鑫","jys":"sz"},{"dm":"002113","mc":"*ST天润","jys":"sz"},{"dm":"002076","mc":"星光股份","jys":"sz"},{"dm":"002071","mc":"长城退","jys":"sz"},{"dm":"002070","mc":"众和退","jys":"sz"},{"dm":"002064","mc":"华峰化学","jys":"sz"},{"dm":"002062","mc":"宏润建设","jys":"sz"},{"dm":"002054","mc":"德美化工","jys":"sz"},{"dm":"002049","mc":"紫光国微","jys":"sz"},{"dm":"002038","mc":"双鹭药业","jys":"sz"},{"dm":"002037","mc":"保利联合","jys":"sz"},{"dm":"002030","mc":"达安基因","jys":"sz"},{"dm":"002024","mc":"ST易购","jys":"sz"},{"dm":"002018","mc":"华信退","jys":"sz"},{"dm":"002013","mc":"中航机电","jys":"sz"},{"dm":"002005","mc":"ST德豪","jys":"sz"},{"dm":"001326","mc":"联域股份","jys":"sz"},{"dm":"001306","mc":"夏厦精密","jys":"sz"},{"dm":"001234","mc":"泰慕士","jys":"sz"},{"dm":"001222","mc":"源飞宠物","jys":"sz"},{"dm":"001219","mc":"青岛食品","jys":"sz"},{"dm":"001218","mc":"丽臣实业","jys":"sz"},{"dm":"000987","mc":"越秀资本","jys":"sz"},{"dm":"000982","mc":"中银绒业","jys":"sz"},{"dm":"000979","mc":"中弘退","jys":"sz"},{"dm":"000972","mc":"中基健康","jys":"sz"},{"dm":"000965","mc":"天保基建","jys":"sz"},{"dm":"000956","mc":"中原油气","jys":"sz"},{"dm":"000939","mc":"凯迪退","jys":"sz"},{"dm":"000926","mc":"福星股份","jys":"sz"},{"dm":"000918","mc":"*ST嘉凯","jys":"sz"},{"dm":"000916","mc":"华北高速","jys":"sz"},{"dm":"000903","mc":"云内动力","jys":"sz"},{"dm":"000876","mc":"新 希 望","jys":"sz"},{"dm":"000866","mc":"扬子石化","jys":"sz"},{"dm":"000852","mc":"石化机械","jys":"sz"},{"dm":"000839","mc":"ST国安","jys":"sz"},{"dm":"000836","mc":"富通信息","jys":"sz"},{"dm":"000835","mc":"长动退","jys":"sz"},{"dm":"000832","mc":"*ST龙涤","jys":"sz"},{"dm":"000827","mc":"*ST长兴","jys":"sz"},{"dm":"000817","mc":"辽河油田","jys":"sz"},{"dm":"000806","mc":"银河退","jys":"sz"},{"dm":"000805","mc":"*ST炎黄","jys":"sz"},{"dm":"000800","mc":"一汽解放","jys":"sz"},{"dm":"000787","mc":"*ST创智","jys":"sz"},{"dm":"000780","mc":"ST平能","jys":"sz"},{"dm":"000769","mc":"*ST大菲","jys":"sz"},{"dm":"000765","mc":"*ST华信","jys":"sz"},{"dm":"000763","mc":"锦州石化","jys":"sz"},{"dm":"000760","mc":"斯太退","jys":"sz"},{"dm":"000751","mc":"锌业股份","jys":"sz"},{"dm":"000748","mc":"长城信息","jys":"sz"},{"dm":"000736","mc":"中交地产","jys":"sz"},{"dm":"000732","mc":"ST泰禾","jys":"sz"},{"dm":"000730","mc":"*ST环保","jys":"sz"},{"dm":"000728","mc":"国元证券","jys":"sz"},{"dm":"000726","mc":"鲁 泰A","jys":"sz"},{"dm":"000699","mc":"S*ST佳纸","jys":"sz"},{"dm":"000693","mc":"华泽退","jys":"sz"},{"dm":"000689","mc":"ST宏业","jys":"sz"},{"dm":"000687","mc":"华讯退","jys":"sz"},{"dm":"000675","mc":"ST银山","jys":"sz"},{"dm":"000673","mc":"当代退","jys":"sz"},{"dm":"000671","mc":"ST阳光城","jys":"sz"},{"dm":"000667","mc":"ST美置","jys":"sz"},{"dm":"000666","mc":"经纬纺机","jys":"sz"},{"dm":"000662","mc":"天夏退","jys":"sz"},{"dm":"000660","mc":"*ST南华","jys":"sz"},{"dm":"000658","mc":"ST海洋","jys":"sz"},{"dm":"000653","mc":"ST九州","jys":"sz"},{"dm":"000626","mc":"远大控股","jys":"sz"},{"dm":"000621","mc":"*ST比特","jys":"sz"},{"dm":"000618","mc":"吉林化工","jys":"sz"},{"dm":"000616","mc":"*ST海投","jys":"sz"},{"dm":"000613","mc":"东海A退","jys":"sz"},{"dm":"000611","mc":"天首退","jys":"sz"},{"dm":"000606","mc":"顺利退","jys":"sz"},{"dm":"000602","mc":"金马集团","jys":"sz"},{"dm":"000601","mc":"韶能股份","jys":"sz"},{"dm":"000595","mc":"宝塔实业","jys":"sz"},{"dm":"000594","mc":"国恒退","jys":"sz"},{"dm":"000588","mc":"PT粤金曼","jys":"sz"},{"dm":"000587","mc":"*ST金洲","jys":"sz"},{"dm":"000585","mc":"东电退","jys":"sz"},{"dm":"000583","mc":"S*ST托普","jys":"sz"},{"dm":"000578","mc":"盐湖集团","jys":"sz"},{"dm":"000573","mc":"粤宏远A","jys":"sz"},{"dm":"000569","mc":"长城股份","jys":"sz"},{"dm":"000566","mc":"海南海药","jys":"sz"},{"dm":"000564","mc":"ST大集","jys":"sz"},{"dm":"000563","mc":"陕国投A","jys":"sz"},{"dm":"000562","mc":"宏源证券","jys":"sz"},{"dm":"000558","mc":"莱茵体育","jys":"sz"},{"dm":"000556","mc":"PT南洋","jys":"sz"},{"dm":"000552","mc":"甘肃能化","jys":"sz"},{"dm":"000549","mc":"S湘火炬","jys":"sz"},{"dm":"000542","mc":"TCL通讯","jys":"sz"},{"dm":"000540","mc":"*ST中天","jys":"sz"},{"dm":"000539","mc":"粤电力A","jys":"sz"},{"dm":"000535","mc":"*ST猴王","jys":"sz"},{"dm":"000527","mc":"美的电器","jys":"sz"},{"dm":"000522","mc":"白云山A","jys":"sz"},{"dm":"000518","mc":"四环生物","jys":"sz"},{"dm":"000515","mc":"攀渝钛业","jys":"sz"},{"dm":"000511","mc":"烯碳退","jys":"sz"},{"dm":"000508","mc":"琼民源A","jys":"sz"},{"dm":"000505","mc":"京粮控股","jys":"sz"},{"dm":"000502","mc":"绿景退","jys":"sz"},{"dm":"000418","mc":"小天鹅A","jys":"sz"},{"dm":"000412","mc":"ST五环","jys":"sz"},{"dm":"000406","mc":"石油大明","jys":"sz"},{"dm":"000405","mc":"ST鑫光","jys":"sz"},{"dm":"000404","mc":"长虹华意","jys":"sz"},{"dm":"000153","mc":"丰原药业","jys":"sz"},{"dm":"000150","mc":"*ST宜康","jys":"sz"},{"dm":"000100","mc":"TCL科技","jys":"sz"},{"dm":"000088","mc":"盐 田 港","jys":"sz"},{"dm":"000047","mc":"ST中侨","jys":"sz"},{"dm":"000038","mc":"大通退","jys":"sz"},{"dm":"000037","mc":"深南电A","jys":"sz"},{"dm":"000033","mc":"新都退","jys":"sz"},{"dm":"000024","mc":"招商地产","jys":"sz"},{"dm":"000018","mc":"神城A退","jys":"sz"},{"dm":"000015","mc":"PT中浩A","jys":"sz"},{"dm":"000013","mc":"*ST石化A","jys":"sz"},{"dm":"000012","mc":"南 玻A","jys":"sz"},{"dm":"000005","mc":"ST星源","jys":"sz"},{"dm":"000003","mc":"PT金田A","jys":"sz"},{"dm":"600941","mc":"中国移动","jys":"sh"},{"dm":"002991","mc":"甘源食品","jys":"sz"},{"dm":"300492","mc":"华图山鼎","jys":"sz"},{"dm":"301051","mc":"信濠光电","jys":"sz"},{"dm":"003043","mc":"华亚智能","jys":"sz"},{"dm":"688671","mc":"碧兴物联","jys":"sh"},{"dm":"688419","mc":"耐科装备","jys":"sh"},{"dm":"688232","mc":"新点软件","jys":"sh"},{"dm":"301170","mc":"锡南科技","jys":"sz"},{"dm":"300827","mc":"上能电气","jys":"sz"},{"dm":"603888","mc":"新华网","jys":"sh"},{"dm":"301045","mc":"天禄科技","jys":"sz"},{"dm":"688711","mc":"宏微科技","jys":"sh"},{"dm":"300816","mc":"艾可蓝","jys":"sz"},{"dm":"688359","mc":"三孚新科","jys":"sh"},{"dm":"301279","mc":"金道科技","jys":"sz"},{"dm":"301135","mc":"瑞德智能","jys":"sz"},{"dm":"603276","mc":"恒兴新材","jys":"sh"},{"dm":"688231","mc":"隆达股份","jys":"sh"},{"dm":"301096","mc":"百诚医药","jys":"sz"},{"dm":"002459","mc":"晶澳科技","jys":"sz"},{"dm":"301336","mc":"趣睡科技","jys":"sz"},{"dm":"300907","mc":"康平科技","jys":"sz"},{"dm":"000756","mc":"新华制药","jys":"sz"},{"dm":"301395","mc":"仁信新材","jys":"sz"},{"dm":"300602","mc":"飞荣达","jys":"sz"},{"dm":"603170","mc":"宝立食品","jys":"sh"},{"dm":"603917","mc":"合力科技","jys":"sh"},{"dm":"301125","mc":"腾亚精工","jys":"sz"},{"dm":"301115","mc":"建科股份","jys":"sz"},{"dm":"002897","mc":"意华股份","jys":"sz"},{"dm":"301006","mc":"迈拓股份","jys":"sz"},{"dm":"600055","mc":"万东医疗","jys":"sh"},{"dm":"002935","mc":"天奥电子","jys":"sz"},{"dm":"301079","mc":"邵阳液压","jys":"sz"},{"dm":"601607","mc":"上海医药","jys":"sh"},{"dm":"603639","mc":"海利尔","jys":"sh"},{"dm":"300416","mc":"苏试试验","jys":"sz"},{"dm":"301446","mc":"福事特","jys":"sz"},{"dm":"300946","mc":"恒而达","jys":"sz"},{"dm":"002961","mc":"瑞达期货","jys":"sz"},{"dm":"300777","mc":"中简科技","jys":"sz"},{"dm":"603171","mc":"税友股份","jys":"sh"},{"dm":"300932","mc":"三友联众","jys":"sz"},{"dm":"688361","mc":"中科飞测-U","jys":"sh"},{"dm":"600378","mc":"昊华科技","jys":"sh"},{"dm":"301130","mc":"西点药业","jys":"sz"},{"dm":"603151","mc":"邦基科技","jys":"sh"},{"dm":"002034","mc":"旺能环境","jys":"sz"},{"dm":"000951","mc":"中国重汽","jys":"sz"},{"dm":"600706","mc":"曲江文旅","jys":"sh"},{"dm":"600884","mc":"杉杉股份","jys":"sh"},{"dm":"002859","mc":"洁美科技","jys":"sz"},{"dm":"600089","mc":"特变电工","jys":"sh"},{"dm":"600760","mc":"中航沈飞","jys":"sh"},{"dm":"300800","mc":"力合科技","jys":"sz"},{"dm":"603701","mc":"德宏股份","jys":"sh"},{"dm":"600508","mc":"上海能源","jys":"sh"},{"dm":"600487","mc":"亨通光电","jys":"sh"},{"dm":"300396","mc":"迪瑞医疗","jys":"sz"},{"dm":"000333","mc":"美的集团","jys":"sz"},{"dm":"002943","mc":"宇晶股份","jys":"sz"},{"dm":"000777","mc":"中核科技","jys":"sz"},{"dm":"300942","mc":"易瑞生物","jys":"sz"},{"dm":"301388","mc":"欣灵电气","jys":"sz"},{"dm":"300792","mc":"壹网壹创","jys":"sz"},{"dm":"003025","mc":"思进智能","jys":"sz"},{"dm":"300340","mc":"科恒股份","jys":"sz"},{"dm":"603890","mc":"春秋电子","jys":"sh"},{"dm":"000567","mc":"海德股份","jys":"sz"},{"dm":"600847","mc":"万里股份","jys":"sh"},{"dm":"002883","mc":"中设股份","jys":"sz"},{"dm":"001378","mc":"德冠新材","jys":"sz"},{"dm":"601012","mc":"隆基绿能","jys":"sh"},{"dm":"600060","mc":"海信视像","jys":"sh"},{"dm":"300409","mc":"道氏技术","jys":"sz"},{"dm":"300462","mc":"华铭智能","jys":"sz"},{"dm":"301353","mc":"普莱得","jys":"sz"},{"dm":"002493","mc":"荣盛石化","jys":"sz"},{"dm":"301335","mc":"天元宠物","jys":"sz"},{"dm":"603057","mc":"紫燕食品","jys":"sh"},{"dm":"603213","mc":"镇洋发展","jys":"sh"},{"dm":"002458","mc":"益生股份","jys":"sz"},{"dm":"600048","mc":"保利发展","jys":"sh"},{"dm":"603307","mc":"扬州金泉","jys":"sh"},{"dm":"000999","mc":"华润三九","jys":"sz"},{"dm":"300771","mc":"智莱科技","jys":"sz"},{"dm":"688511","mc":"天微电子","jys":"sh"},{"dm":"300980","mc":"祥源新材","jys":"sz"},{"dm":"000688","mc":"国城矿业","jys":"sz"},{"dm":"002677","mc":"浙江美大","jys":"sz"},{"dm":"603043","mc":"广州酒家","jys":"sh"},{"dm":"600645","mc":"中源协和","jys":"sh"},{"dm":"688330","mc":"宏力达","jys":"sh"},{"dm":"600223","mc":"福瑞达","jys":"sh"},{"dm":"003029","mc":"吉大正元","jys":"sz"},{"dm":"002405","mc":"四维图新","jys":"sz"},{"dm":"301002","mc":"崧盛股份","jys":"sz"},{"dm":"002598","mc":"山东章鼓","jys":"sz"},{"dm":"002920","mc":"德赛西威","jys":"sz"},{"dm":"688035","mc":"德邦科技","jys":"sh"},{"dm":"603038","mc":"华立股份","jys":"sh"},{"dm":"688788","mc":"科思科技","jys":"sh"},{"dm":"002706","mc":"良信股份","jys":"sz"},{"dm":"600099","mc":"林海股份","jys":"sh"},{"dm":"601100","mc":"恒立液压","jys":"sh"},{"dm":"688663","mc":"新风光","jys":"sh"},{"dm":"601177","mc":"杭齿前进","jys":"sh"},{"dm":"300265","mc":"通光线缆","jys":"sz"},{"dm":"603150","mc":"万朗磁塑","jys":"sh"},{"dm":"002349","mc":"精华制药","jys":"sz"},{"dm":"600995","mc":"南网储能","jys":"sh"},{"dm":"001256","mc":"炜冈科技","jys":"sz"},{"dm":"603345","mc":"安井食品","jys":"sh"},{"dm":"601949","mc":"中国出版","jys":"sh"},{"dm":"002824","mc":"和胜股份","jys":"sz"},{"dm":"688320","mc":"禾川科技","jys":"sh"},{"dm":"002955","mc":"鸿合科技","jys":"sz"},{"dm":"300271","mc":"华宇软件","jys":"sz"},{"dm":"300902","mc":"国安达","jys":"sz"},{"dm":"000635","mc":"英 力 特","jys":"sz"},{"dm":"300541","mc":"先进数通","jys":"sz"},{"dm":"000561","mc":"烽火电子","jys":"sz"},{"dm":"603789","mc":"星光农机","jys":"sh"},{"dm":"600480","mc":"凌云股份","jys":"sh"},{"dm":"600158","mc":"中体产业","jys":"sh"},{"dm":"603080","mc":"新疆火炬","jys":"sh"},{"dm":"301167","mc":"建研设计","jys":"sz"},{"dm":"600062","mc":"华润双鹤","jys":"sh"},{"dm":"002916","mc":"深南电路","jys":"sz"},{"dm":"300613","mc":"富瀚微","jys":"sz"},{"dm":"002836","mc":"新宏泽","jys":"sz"},{"dm":"600101","mc":"明星电力","jys":"sh"},{"dm":"000713","mc":"丰乐种业","jys":"sz"},{"dm":"300030","mc":"阳普医疗","jys":"sz"},{"dm":"600037","mc":"歌华有线","jys":"sh"},{"dm":"300599","mc":"雄塑科技","jys":"sz"},{"dm":"688321","mc":"微芯生物","jys":"sh"},{"dm":"600834","mc":"申通地铁","jys":"sh"},{"dm":"002254","mc":"泰和新材","jys":"sz"},{"dm":"601519","mc":"大智慧","jys":"sh"},{"dm":"300617","mc":"安靠智电","jys":"sz"},{"dm":"600143","mc":"金发科技","jys":"sh"},{"dm":"300261","mc":"雅本化学","jys":"sz"},{"dm":"688347","mc":"华虹公司","jys":"sh"},{"dm":"605186","mc":"健麾信息","jys":"sh"},{"dm":"000935","mc":"四川双马","jys":"sz"},{"dm":"002200","mc":"ST交投","jys":"sz"},{"dm":"688082","mc":"盛美上海","jys":"sh"},{"dm":"603167","mc":"渤海轮渡","jys":"sh"},{"dm":"300618","mc":"寒锐钴业","jys":"sz"},{"dm":"301099","mc":"雅创电子","jys":"sz"},{"dm":"002041","mc":"登海种业","jys":"sz"},{"dm":"000776","mc":"广发证券","jys":"sz"},{"dm":"300871","mc":"回盛生物","jys":"sz"},{"dm":"688523","mc":"航天环宇","jys":"sh"},{"dm":"688230","mc":"芯导科技","jys":"sh"},{"dm":"000685","mc":"中山公用","jys":"sz"},{"dm":"000881","mc":"中广核技","jys":"sz"},{"dm":"605399","mc":"晨光新材","jys":"sh"},{"dm":"300972","mc":"万辰集团","jys":"sz"},{"dm":"605055","mc":"迎丰股份","jys":"sh"},{"dm":"000906","mc":"浙商中拓","jys":"sz"},{"dm":"002937","mc":"兴瑞科技","jys":"sz"},{"dm":"600522","mc":"中天科技","jys":"sh"},{"dm":"300016","mc":"北陆药业","jys":"sz"},{"dm":"301075","mc":"多瑞医药","jys":"sz"},{"dm":"002402","mc":"和而泰","jys":"sz"},{"dm":"002768","mc":"国恩股份","jys":"sz"},{"dm":"000905","mc":"厦门港务","jys":"sz"},{"dm":"002451","mc":"摩恩电气","jys":"sz"},{"dm":"601801","mc":"皖新传媒","jys":"sh"},{"dm":"600230","mc":"沧州大化","jys":"sh"},{"dm":"002452","mc":"长高电新","jys":"sz"},{"dm":"002668","mc":"奥马电器","jys":"sz"},{"dm":"600586","mc":"金晶科技","jys":"sh"},{"dm":"300829","mc":"金丹科技","jys":"sz"},{"dm":"002114","mc":"罗平锌电","jys":"sz"},{"dm":"300290","mc":"荣科科技","jys":"sz"},{"dm":"603303","mc":"得邦照明","jys":"sh"},{"dm":"000978","mc":"桂林旅游","jys":"sz"},{"dm":"002301","mc":"齐心集团","jys":"sz"},{"dm":"600456","mc":"宝钛股份","jys":"sh"},{"dm":"002837","mc":"英维克","jys":"sz"},{"dm":"301126","mc":"达嘉维康","jys":"sz"},{"dm":"300232","mc":"洲明科技","jys":"sz"},{"dm":"002747","mc":"埃斯顿","jys":"sz"},{"dm":"688616","mc":"西力科技","jys":"sh"},{"dm":"603016","mc":"新宏泰","jys":"sh"},{"dm":"600141","mc":"兴发集团","jys":"sh"},{"dm":"603358","mc":"华达科技","jys":"sh"},{"dm":"000683","mc":"远兴能源","jys":"sz"},{"dm":"600057","mc":"厦门象屿","jys":"sh"},{"dm":"688130","mc":"晶华微","jys":"sh"},{"dm":"688151","mc":"华强科技","jys":"sh"},{"dm":"603658","mc":"安图生物","jys":"sh"},{"dm":"002571","mc":"德力股份","jys":"sz"},{"dm":"603185","mc":"弘元绿能","jys":"sh"},{"dm":"002142","mc":"宁波银行","jys":"sz"},{"dm":"688023","mc":"安恒信息","jys":"sh"},{"dm":"603177","mc":"德创环保","jys":"sh"},{"dm":"301027","mc":"华蓝集团","jys":"sz"},{"dm":"688003","mc":"天准科技","jys":"sh"},{"dm":"002990","mc":"盛视科技","jys":"sz"},{"dm":"300911","mc":"亿田智能","jys":"sz"},{"dm":"600778","mc":"友好集团","jys":"sh"},{"dm":"600446","mc":"金证股份","jys":"sh"},{"dm":"300018","mc":"中元股份","jys":"sz"},{"dm":"600845","mc":"宝信软件","jys":"sh"},{"dm":"002557","mc":"洽洽食品","jys":"sz"},{"dm":"002103","mc":"广博股份","jys":"sz"},{"dm":"600395","mc":"盘江股份","jys":"sh"},{"dm":"301009","mc":"可靠股份","jys":"sz"},{"dm":"002272","mc":"川润股份","jys":"sz"},{"dm":"601279","mc":"英利汽车","jys":"sh"},{"dm":"603225","mc":"新凤鸣","jys":"sh"},{"dm":"301356","mc":"天振股份","jys":"sz"},{"dm":"300417","mc":"南华仪器","jys":"sz"},{"dm":"002340","mc":"格林美","jys":"sz"},{"dm":"001230","mc":"劲旅环境","jys":"sz"},{"dm":"603280","mc":"南方路机","jys":"sh"},{"dm":"000668","mc":"荣丰控股","jys":"sz"},{"dm":"688155","mc":"先惠技术","jys":"sh"},{"dm":"600183","mc":"生益科技","jys":"sh"},{"dm":"601136","mc":"首创证券","jys":"sh"},{"dm":"600516","mc":"方大炭素","jys":"sh"},{"dm":"000498","mc":"山东路桥","jys":"sz"},{"dm":"600180","mc":"瑞茂通","jys":"sh"},{"dm":"002529","mc":"海源复材","jys":"sz"},{"dm":"601098","mc":"中南传媒","jys":"sh"},{"dm":"601825","mc":"沪农商行","jys":"sh"},{"dm":"301265","mc":"华新环保","jys":"sz"},{"dm":"600030","mc":"中信证券","jys":"sh"},{"dm":"600406","mc":"国电南瑞","jys":"sh"},{"dm":"603021","mc":"山东华鹏","jys":"sh"},{"dm":"002170","mc":"芭田股份","jys":"sz"},{"dm":"605169","mc":"洪通燃气","jys":"sh"},{"dm":"600015","mc":"华夏银行","jys":"sh"},{"dm":"000837","mc":"秦川机床","jys":"sz"},{"dm":"001201","mc":"东瑞股份","jys":"sz"},{"dm":"301309","mc":"万得凯","jys":"sz"},{"dm":"000791","mc":"甘肃能源","jys":"sz"},{"dm":"688680","mc":"海优新材","jys":"sh"},{"dm":"605111","mc":"新洁能","jys":"sh"},{"dm":"301087","mc":"可孚医疗","jys":"sz"},{"dm":"603683","mc":"晶华新材","jys":"sh"},{"dm":"603211","mc":"晋拓股份","jys":"sh"},{"dm":"000932","mc":"华菱钢铁","jys":"sz"},{"dm":"300772","mc":"运达股份","jys":"sz"},{"dm":"601339","mc":"百隆东方","jys":"sh"},{"dm":"301109","mc":"军信股份","jys":"sz"},{"dm":"301119","mc":"正强股份","jys":"sz"},{"dm":"601366","mc":"利群股份","jys":"sh"},{"dm":"002347","mc":"泰尔股份","jys":"sz"},{"dm":"301468","mc":"博盈特焊","jys":"sz"},{"dm":"603508","mc":"思维列控","jys":"sh"},{"dm":"600660","mc":"福耀玻璃","jys":"sh"},{"dm":"002681","mc":"奋达科技","jys":"sz"},{"dm":"002627","mc":"三峡旅游","jys":"sz"},{"dm":"002060","mc":"粤 水 电","jys":"sz"},{"dm":"002415","mc":"海康威视","jys":"sz"},{"dm":"600916","mc":"中国黄金","jys":"sh"},{"dm":"300867","mc":"圣元环保","jys":"sz"},{"dm":"002507","mc":"涪陵榨菜","jys":"sz"},{"dm":"600193","mc":"创兴资源","jys":"sh"},{"dm":"002438","mc":"江苏神通","jys":"sz"},{"dm":"000957","mc":"中通客车","jys":"sz"},{"dm":"002573","mc":"清新环境","jys":"sz"},{"dm":"300651","mc":"金陵体育","jys":"sz"},{"dm":"002485","mc":"*ST雪发","jys":"sz"},{"dm":"605183","mc":"确成股份","jys":"sh"},{"dm":"601211","mc":"国泰君安","jys":"sh"},{"dm":"605488","mc":"福莱新材","jys":"sh"},{"dm":"002365","mc":"永安药业","jys":"sz"},{"dm":"600740","mc":"山西焦化","jys":"sh"},{"dm":"605358","mc":"立昂微","jys":"sh"},{"dm":"688385","mc":"复旦微电","jys":"sh"},{"dm":"002090","mc":"金智科技","jys":"sz"},{"dm":"600750","mc":"江中药业","jys":"sh"},{"dm":"601700","mc":"风范股份","jys":"sh"},{"dm":"600459","mc":"贵研铂业","jys":"sh"},{"dm":"002922","mc":"伊戈尔","jys":"sz"},{"dm":"002917","mc":"金奥博","jys":"sz"},{"dm":"002688","mc":"金河生物","jys":"sz"},{"dm":"603060","mc":"国检集团","jys":"sh"},{"dm":"002462","mc":"嘉事堂","jys":"sz"},{"dm":"300021","mc":"大禹节水","jys":"sz"},{"dm":"000158","mc":"常山北明","jys":"sz"},{"dm":"002611","mc":"东方精工","jys":"sz"},{"dm":"000417","mc":"合肥百货","jys":"sz"},{"dm":"002989","mc":"中天精装","jys":"sz"},{"dm":"002004","mc":"华邦健康","jys":"sz"},{"dm":"600901","mc":"江苏金租","jys":"sh"},{"dm":"002497","mc":"雅化集团","jys":"sz"},{"dm":"002441","mc":"众业达","jys":"sz"},{"dm":"000993","mc":"闽东电力","jys":"sz"},{"dm":"002025","mc":"航天电器","jys":"sz"},{"dm":"603587","mc":"地素时尚","jys":"sh"},{"dm":"601233","mc":"桐昆股份","jys":"sh"},{"dm":"301246","mc":"宏源药业","jys":"sz"},{"dm":"002232","mc":"启明信息","jys":"sz"},{"dm":"301500","mc":"飞南资源","jys":"sz"},{"dm":"688549","mc":"中巨芯-U","jys":"sh"},{"dm":"300959","mc":"线上线下","jys":"sz"},{"dm":"600217","mc":"中再资环","jys":"sh"},{"dm":"688122","mc":"西部超导","jys":"sh"},{"dm":"002778","mc":"中晟高科","jys":"sz"},{"dm":"300529","mc":"健帆生物","jys":"sz"},{"dm":"600382","mc":"广东明珠","jys":"sh"},{"dm":"600857","mc":"宁波中百","jys":"sh"},{"dm":"600058","mc":"五矿发展","jys":"sh"},{"dm":"600303","mc":"ST曙光","jys":"sh"},{"dm":"000758","mc":"中色股份","jys":"sz"},{"dm":"600711","mc":"盛屯矿业","jys":"sh"},{"dm":"600251","mc":"冠农股份","jys":"sh"},{"dm":"605162","mc":"新中港","jys":"sh"},{"dm":"300094","mc":"国联水产","jys":"sz"},{"dm":"603601","mc":"再升科技","jys":"sh"},{"dm":"300623","mc":"捷捷微电","jys":"sz"},{"dm":"300026","mc":"红日药业","jys":"sz"},{"dm":"301238","mc":"瑞泰新材","jys":"sz"},{"dm":"300481","mc":"濮阳惠成","jys":"sz"},{"dm":"688166","mc":"博瑞医药","jys":"sh"},{"dm":"601598","mc":"中国外运","jys":"sh"},{"dm":"600123","mc":"兰花科创","jys":"sh"},{"dm":"600197","mc":"伊力特","jys":"sh"},{"dm":"600538","mc":"国发股份","jys":"sh"},{"dm":"300106","mc":"西部牧业","jys":"sz"},{"dm":"000733","mc":"振华科技","jys":"sz"},{"dm":"601727","mc":"上海电气","jys":"sh"},{"dm":"688375","mc":"国博电子","jys":"sh"},{"dm":"002162","mc":"悦心健康","jys":"sz"},{"dm":"300753","mc":"爱朋医疗","jys":"sz"},{"dm":"000759","mc":"中百集团","jys":"sz"},{"dm":"000912","mc":"泸天化","jys":"sz"},{"dm":"301053","mc":"远信工业","jys":"sz"},{"dm":"603858","mc":"步长制药","jys":"sh"},{"dm":"603866","mc":"桃李面包","jys":"sh"},{"dm":"300287","mc":"飞利信","jys":"sz"},{"dm":"603323","mc":"苏农银行","jys":"sh"},{"dm":"002218","mc":"拓日新能","jys":"sz"},{"dm":"002057","mc":"中钢天源","jys":"sz"},{"dm":"688120","mc":"华海清科","jys":"sh"},{"dm":"688032","mc":"禾迈股份","jys":"sh"},{"dm":"300919","mc":"中伟股份","jys":"sz"},{"dm":"002327","mc":"富安娜","jys":"sz"},{"dm":"603583","mc":"捷昌驱动","jys":"sh"},{"dm":"300813","mc":"泰林生物","jys":"sz"},{"dm":"300477","mc":"合纵科技","jys":"sz"},{"dm":"002342","mc":"巨力索具","jys":"sz"},{"dm":"603093","mc":"南华期货","jys":"sh"},{"dm":"301256","mc":"华融化学","jys":"sz"},{"dm":"300733","mc":"西菱动力","jys":"sz"},{"dm":"000883","mc":"湖北能源","jys":"sz"},{"dm":"301518","mc":"长华化学","jys":"sz"},{"dm":"300423","mc":"昇辉科技","jys":"sz"},{"dm":"001299","mc":"美能能源","jys":"sz"},{"dm":"600148","mc":"长春一东","jys":"sh"},{"dm":"600096","mc":"云天化","jys":"sh"},{"dm":"001213","mc":"中铁特货","jys":"sz"},{"dm":"002108","mc":"沧州明珠","jys":"sz"},{"dm":"000958","mc":"电投产融","jys":"sz"},{"dm":"001337","mc":"四川黄金","jys":"sz"},{"dm":"600903","mc":"贵州燃气","jys":"sh"},{"dm":"603368","mc":"柳药集团","jys":"sh"},{"dm":"600617","mc":"国新能源","jys":"sh"},{"dm":"301559","mc":"中集环科","jys":"sz"},{"dm":"002562","mc":"兄弟科技","jys":"sz"},{"dm":"600956","mc":"新天绿能","jys":"sh"},{"dm":"002296","mc":"辉煌科技","jys":"sz"},{"dm":"002001","mc":"新 和 成","jys":"sz"},{"dm":"301069","mc":"凯盛新材","jys":"sz"},{"dm":"300490","mc":"华自科技","jys":"sz"},{"dm":"000900","mc":"现代投资","jys":"sz"},{"dm":"002774","mc":"快意电梯","jys":"sz"},{"dm":"688777","mc":"中控技术","jys":"sh"},{"dm":"601077","mc":"渝农商行","jys":"sh"},{"dm":"000428","mc":"华天酒店","jys":"sz"},{"dm":"603619","mc":"中曼石油","jys":"sh"},{"dm":"000725","mc":"京东方A","jys":"sz"},{"dm":"301234","mc":"五洲医疗","jys":"sz"},{"dm":"003039","mc":"顺控发展","jys":"sz"},{"dm":"301177","mc":"迪阿股份","jys":"sz"},{"dm":"601156","mc":"东航物流","jys":"sh"},{"dm":"301310","mc":"鑫宏业","jys":"sz"},{"dm":"600790","mc":"轻纺城","jys":"sh"},{"dm":"301100","mc":"风光股份","jys":"sz"},{"dm":"600425","mc":"青松建化","jys":"sh"},{"dm":"601877","mc":"正泰电器","jys":"sh"},{"dm":"301098","mc":"金埔园林","jys":"sz"},{"dm":"300005","mc":"探路者","jys":"sz"},{"dm":"001278","mc":"一彬科技","jys":"sz"},{"dm":"603956","mc":"威派格","jys":"sh"},{"dm":"301025","mc":"读客文化","jys":"sz"},{"dm":"300915","mc":"海融科技","jys":"sz"},{"dm":"603843","mc":"正平股份","jys":"sh"},{"dm":"002498","mc":"汉缆股份","jys":"sz"},{"dm":"002172","mc":"澳洋健康","jys":"sz"},{"dm":"600299","mc":"安迪苏","jys":"sh"},{"dm":"688018","mc":"乐鑫科技","jys":"sh"},{"dm":"002339","mc":"积成电子","jys":"sz"},{"dm":"603351","mc":"威尔药业","jys":"sh"},{"dm":"002186","mc":"全 聚 德","jys":"sz"},{"dm":"605007","mc":"五洲特纸","jys":"sh"},{"dm":"600961","mc":"株冶集团","jys":"sh"},{"dm":"603657","mc":"春光科技","jys":"sh"},{"dm":"300452","mc":"山河药辅","jys":"sz"},{"dm":"300500","mc":"启迪设计","jys":"sz"},{"dm":"002061","mc":"浙江交科","jys":"sz"},{"dm":"601827","mc":"三峰环境","jys":"sh"},{"dm":"601061","mc":"中信金属","jys":"sh"},{"dm":"300043","mc":"星辉娱乐","jys":"sz"},{"dm":"002568","mc":"百润股份","jys":"sz"},{"dm":"688560","mc":"明冠新材","jys":"sh"},{"dm":"601116","mc":"三江购物","jys":"sh"},{"dm":"300686","mc":"智动力","jys":"sz"},{"dm":"002266","mc":"浙富控股","jys":"sz"},{"dm":"002056","mc":"横店东磁","jys":"sz"},{"dm":"002984","mc":"森麒麟","jys":"sz"},{"dm":"603317","mc":"天味食品","jys":"sh"},{"dm":"300569","mc":"天能重工","jys":"sz"},{"dm":"300037","mc":"新宙邦","jys":"sz"},{"dm":"600828","mc":"茂业商业","jys":"sh"},{"dm":"300246","mc":"宝莱特","jys":"sz"},{"dm":"688265","mc":"南模生物","jys":"sh"},{"dm":"600746","mc":"江苏索普","jys":"sh"},{"dm":"688187","mc":"时代电气","jys":"sh"},{"dm":"688778","mc":"厦钨新能","jys":"sh"},{"dm":"003028","mc":"振邦智能","jys":"sz"},{"dm":"600137","mc":"浪莎股份","jys":"sh"},{"dm":"002807","mc":"江阴银行","jys":"sz"},{"dm":"603997","mc":"继峰股份","jys":"sh"},{"dm":"002695","mc":"煌上煌","jys":"sz"},{"dm":"300208","mc":"青岛中程","jys":"sz"},{"dm":"000670","mc":"盈方微","jys":"sz"},{"dm":"300486","mc":"东杰智能","jys":"sz"},{"dm":"002484","mc":"江海股份","jys":"sz"},{"dm":"300056","mc":"中创环保","jys":"sz"},{"dm":"688252","mc":"天德钰","jys":"sh"},{"dm":"002216","mc":"三全食品","jys":"sz"},{"dm":"301228","mc":"实朴检测","jys":"sz"},{"dm":"603698","mc":"航天工程","jys":"sh"},{"dm":"600346","mc":"恒力石化","jys":"sh"},{"dm":"601985","mc":"中国核电","jys":"sh"},{"dm":"300680","mc":"隆盛科技","jys":"sz"},{"dm":"002589","mc":"瑞康医药","jys":"sz"},{"dm":"688733","mc":"壹石通","jys":"sh"},{"dm":"603218","mc":"日月股份","jys":"sh"},{"dm":"600433","mc":"冠豪高新","jys":"sh"},{"dm":"688046","mc":"药康生物","jys":"sh"},{"dm":"605006","mc":"山东玻纤","jys":"sh"},{"dm":"301216","mc":"万凯新材","jys":"sz"},{"dm":"688226","mc":"威腾电气","jys":"sh"},{"dm":"600308","mc":"华泰股份","jys":"sh"},{"dm":"301300","mc":"远翔新材","jys":"sz"},{"dm":"002252","mc":"上海莱士","jys":"sz"},{"dm":"300887","mc":"谱尼测试","jys":"sz"},{"dm":"600477","mc":"杭萧钢构","jys":"sh"},{"dm":"600073","mc":"上海梅林","jys":"sh"},{"dm":"002116","mc":"中国海诚","jys":"sz"},{"dm":"600876","mc":"凯盛新能","jys":"sh"},{"dm":"301292","mc":"海科新源","jys":"sz"},{"dm":"688305","mc":"科德数控","jys":"sh"},{"dm":"600489","mc":"中金黄金","jys":"sh"},{"dm":"300658","mc":"延江股份","jys":"sz"},{"dm":"688576","mc":"西山科技","jys":"sh"},{"dm":"000650","mc":"仁和药业","jys":"sz"},{"dm":"301509","mc":"金凯生科","jys":"sz"},{"dm":"605598","mc":"上海港湾","jys":"sh"},{"dm":"600966","mc":"博汇纸业","jys":"sh"},{"dm":"603976","mc":"正川股份","jys":"sh"},{"dm":"600939","mc":"重庆建工","jys":"sh"},{"dm":"603335","mc":"迪生力","jys":"sh"},{"dm":"600917","mc":"重庆燃气","jys":"sh"},{"dm":"300326","mc":"凯利泰","jys":"sz"},{"dm":"002565","mc":"顺灏股份","jys":"sz"},{"dm":"002112","mc":"三变科技","jys":"sz"},{"dm":"002067","mc":"景兴纸业","jys":"sz"},{"dm":"600283","mc":"钱江水利","jys":"sh"},{"dm":"300997","mc":"欢乐家","jys":"sz"},{"dm":"603126","mc":"中材节能","jys":"sh"},{"dm":"300532","mc":"今天国际","jys":"sz"},{"dm":"002193","mc":"如意集团","jys":"sz"},{"dm":"603102","mc":"百合股份","jys":"sh"},{"dm":"688168","mc":"安博通","jys":"sh"},{"dm":"003006","mc":"百亚股份","jys":"sz"},{"dm":"002978","mc":"安宁股份","jys":"sz"},{"dm":"603192","mc":"汇得科技","jys":"sh"},{"dm":"002082","mc":"万邦德","jys":"sz"},{"dm":"000042","mc":"中洲控股","jys":"sz"},{"dm":"605001","mc":"威奥股份","jys":"sh"},{"dm":"600731","mc":"湖南海利","jys":"sh"},{"dm":"300519","mc":"新光药业","jys":"sz"},{"dm":"002636","mc":"金安国纪","jys":"sz"},{"dm":"002798","mc":"帝欧家居","jys":"sz"},{"dm":"600377","mc":"宁沪高速","jys":"sh"},{"dm":"002960","mc":"青鸟消防","jys":"sz"},{"dm":"600350","mc":"山东高速","jys":"sh"},{"dm":"000887","mc":"中鼎股份","jys":"sz"},{"dm":"002992","mc":"宝明科技","jys":"sz"},{"dm":"603421","mc":"鼎信通讯","jys":"sh"},{"dm":"688623","mc":"双元科技","jys":"sh"},{"dm":"600094","mc":"大名城","jys":"sh"},{"dm":"603137","mc":"恒尚节能","jys":"sh"},{"dm":"601200","mc":"上海环境","jys":"sh"},{"dm":"000995","mc":"皇台酒业","jys":"sz"},{"dm":"600933","mc":"爱柯迪","jys":"sh"},{"dm":"688459","mc":"哈铁科技","jys":"sh"},{"dm":"000619","mc":"海螺新材","jys":"sz"},{"dm":"300965","mc":"恒宇信通","jys":"sz"},{"dm":"603886","mc":"元祖股份","jys":"sh"},{"dm":"000023","mc":"ST深天","jys":"sz"},{"dm":"605286","mc":"同力日升","jys":"sh"},{"dm":"002938","mc":"鹏鼎控股","jys":"sz"},{"dm":"601228","mc":"广州港","jys":"sh"},{"dm":"000802","mc":"北京文化","jys":"sz"},{"dm":"603817","mc":"海峡环保","jys":"sh"},{"dm":"002849","mc":"威星智能","jys":"sz"},{"dm":"002538","mc":"司尔特","jys":"sz"},{"dm":"600064","mc":"南京高科","jys":"sh"},{"dm":"000078","mc":"海王生物","jys":"sz"},{"dm":"600635","mc":"大众公用","jys":"sh"},{"dm":"002029","mc":"七 匹 狼","jys":"sz"},{"dm":"300768","mc":"迪普科技","jys":"sz"},{"dm":"688011","mc":"新光光电","jys":"sh"},{"dm":"688297","mc":"中无人机","jys":"sh"},{"dm":"300594","mc":"朗进科技","jys":"sz"},{"dm":"300091","mc":"金通灵","jys":"sz"},{"dm":"603156","mc":"养元饮品","jys":"sh"},{"dm":"300983","mc":"尤安设计","jys":"sz"},{"dm":"603628","mc":"清源股份","jys":"sh"},{"dm":"600490","mc":"鹏欣资源","jys":"sh"},{"dm":"603458","mc":"勘设股份","jys":"sh"},{"dm":"600958","mc":"东方证券","jys":"sh"},{"dm":"688589","mc":"力合微","jys":"sh"},{"dm":"603991","mc":"至正股份","jys":"sh"},{"dm":"601399","mc":"国机重装","jys":"sh"},{"dm":"002202","mc":"金风科技","jys":"sz"},{"dm":"002141","mc":"贤丰控股","jys":"sz"},{"dm":"000617","mc":"中油资本","jys":"sz"},{"dm":"688677","mc":"海泰新光","jys":"sh"},{"dm":"600108","mc":"亚盛集团","jys":"sh"},{"dm":"000858","mc":"五 粮 液","jys":"sz"},{"dm":"603916","mc":"苏博特","jys":"sh"},{"dm":"605555","mc":"德昌股份","jys":"sh"},{"dm":"688349","mc":"三一重能","jys":"sh"},{"dm":"301325","mc":"曼恩斯特","jys":"sz"},{"dm":"002191","mc":"劲嘉股份","jys":"sz"},{"dm":"000002","mc":"万 科A","jys":"sz"},{"dm":"600817","mc":"宇通重工","jys":"sh"},{"dm":"600583","mc":"海油工程","jys":"sh"},{"dm":"688319","mc":"欧林生物","jys":"sh"},{"dm":"601818","mc":"光大银行","jys":"sh"},{"dm":"003816","mc":"中国广核","jys":"sz"},{"dm":"603682","mc":"锦和商管","jys":"sh"},{"dm":"688707","mc":"振华新材","jys":"sh"},{"dm":"000411","mc":"英特集团","jys":"sz"},{"dm":"002932","mc":"明德生物","jys":"sz"},{"dm":"688475","mc":"萤石网络","jys":"sh"},{"dm":"002471","mc":"中超控股","jys":"sz"},{"dm":"300511","mc":"雪榕生物","jys":"sz"},{"dm":"688819","mc":"天能股份","jys":"sh"},{"dm":"002817","mc":"黄山胶囊","jys":"sz"},{"dm":"002022","mc":"科华生物","jys":"sz"},{"dm":"300748","mc":"金力永磁","jys":"sz"},{"dm":"603001","mc":"ST奥康","jys":"sh"},{"dm":"603366","mc":"日出东方","jys":"sh"},{"dm":"688569","mc":"铁科轨道","jys":"sh"},{"dm":"002280","mc":"联络互动","jys":"sz"},{"dm":"600562","mc":"国睿科技","jys":"sh"},{"dm":"600547","mc":"山东黄金","jys":"sh"},{"dm":"300888","mc":"稳健医疗","jys":"sz"},{"dm":"601718","mc":"际华集团","jys":"sh"},{"dm":"002703","mc":"浙江世宝","jys":"sz"},{"dm":"002195","mc":"岩山科技","jys":"sz"},{"dm":"002983","mc":"芯瑞达","jys":"sz"},{"dm":"002581","mc":"未名医药","jys":"sz"},{"dm":"603387","mc":"基蛋生物","jys":"sh"},{"dm":"600017","mc":"日照港","jys":"sh"},{"dm":"300159","mc":"新研股份","jys":"sz"},{"dm":"002705","mc":"新宝股份","jys":"sz"},{"dm":"300413","mc":"芒果超媒","jys":"sz"},{"dm":"603668","mc":"天马科技","jys":"sh"},{"dm":"301063","mc":"海锅股份","jys":"sz"},{"dm":"600239","mc":"云南城投","jys":"sh"},{"dm":"002173","mc":"创新医疗","jys":"sz"},{"dm":"300393","mc":"中来股份","jys":"sz"},{"dm":"002157","mc":"*ST 正邦","jys":"sz"},{"dm":"001316","mc":"润贝航科","jys":"sz"},{"dm":"601311","mc":"骆驼股份","jys":"sh"},{"dm":"301305","mc":"朗坤环境","jys":"sz"},{"dm":"600981","mc":"汇鸿集团","jys":"sh"},{"dm":"000610","mc":"西安旅游","jys":"sz"},{"dm":"300769","mc":"德方纳米","jys":"sz"},{"dm":"600325","mc":"华发股份","jys":"sh"},{"dm":"000419","mc":"通程控股","jys":"sz"},{"dm":"601212","mc":"白银有色","jys":"sh"},{"dm":"603215","mc":"比依股份","jys":"sh"},{"dm":"600098","mc":"广州发展","jys":"sh"},{"dm":"600372","mc":"中航机载","jys":"sh"},{"dm":"002386","mc":"天原股份","jys":"sz"},{"dm":"000737","mc":"北方铜业","jys":"sz"},{"dm":"002125","mc":"湘潭电化","jys":"sz"},{"dm":"000721","mc":"西安饮食","jys":"sz"},{"dm":"000517","mc":"荣安地产","jys":"sz"},{"dm":"300791","mc":"仙乐健康","jys":"sz"},{"dm":"300182","mc":"捷成股份","jys":"sz"},{"dm":"000030","mc":"富奥股份","jys":"sz"},{"dm":"002758","mc":"浙农股份","jys":"sz"},{"dm":"301112","mc":"信邦智能","jys":"sz"},{"dm":"301088","mc":"戎美股份","jys":"sz"},{"dm":"300853","mc":"申昊科技","jys":"sz"},{"dm":"002048","mc":"宁波华翔","jys":"sz"},{"dm":"002201","mc":"正威新材","jys":"sz"},{"dm":"002702","mc":"海欣食品","jys":"sz"},{"dm":"688002","mc":"睿创微纳","jys":"sh"},{"dm":"002434","mc":"万里扬","jys":"sz"},{"dm":"002292","mc":"奥飞娱乐","jys":"sz"},{"dm":"300572","mc":"安车检测","jys":"sz"},{"dm":"601996","mc":"丰林集团","jys":"sh"},{"dm":"601086","mc":"国芳集团","jys":"sh"},{"dm":"000559","mc":"万向钱潮","jys":"sz"},{"dm":"603130","mc":"云中马","jys":"sh"},{"dm":"300220","mc":"ST金运","jys":"sz"},{"dm":"002850","mc":"科达利","jys":"sz"},{"dm":"600367","mc":"红星发展","jys":"sh"},{"dm":"002128","mc":"电投能源","jys":"sz"},{"dm":"603155","mc":"新亚强","jys":"sh"},{"dm":"600252","mc":"中恒集团","jys":"sh"},{"dm":"000927","mc":"中国铁物","jys":"sz"},{"dm":"000507","mc":"珠海港","jys":"sz"},{"dm":"002367","mc":"康力电梯","jys":"sz"},{"dm":"002429","mc":"兆驰股份","jys":"sz"},{"dm":"301195","mc":"北路智控","jys":"sz"},{"dm":"002353","mc":"杰瑞股份","jys":"sz"},{"dm":"002285","mc":"世联行","jys":"sz"},{"dm":"605319","mc":"无锡振华","jys":"sh"},{"dm":"002393","mc":"力生制药","jys":"sz"},{"dm":"002250","mc":"联化科技","jys":"sz"},{"dm":"300044","mc":"赛为智能","jys":"sz"},{"dm":"603955","mc":"大千生态","jys":"sh"},{"dm":"301322","mc":"绿通科技","jys":"sz"},{"dm":"688538","mc":"和辉光电-U","jys":"sh"},{"dm":"601728","mc":"中国电信","jys":"sh"},{"dm":"688211","mc":"中科微至","jys":"sh"},{"dm":"300350","mc":"华鹏飞","jys":"sz"},{"dm":"601860","mc":"紫金银行","jys":"sh"},{"dm":"600196","mc":"复星医药","jys":"sh"},{"dm":"002287","mc":"奇正藏药","jys":"sz"},{"dm":"300894","mc":"火星人","jys":"sz"},{"dm":"002697","mc":"红旗连锁","jys":"sz"},{"dm":"000568","mc":"泸州老窖","jys":"sz"},{"dm":"002085","mc":"万丰奥威","jys":"sz"},{"dm":"601528","mc":"瑞丰银行","jys":"sh"},{"dm":"600497","mc":"驰宏锌锗","jys":"sh"},{"dm":"600337","mc":"美克家居","jys":"sh"},{"dm":"300048","mc":"合康新能","jys":"sz"},{"dm":"600582","mc":"天地科技","jys":"sh"},{"dm":"600488","mc":"津药药业","jys":"sh"},{"dm":"000422","mc":"湖北宜化","jys":"sz"},{"dm":"600621","mc":"华鑫股份","jys":"sh"},{"dm":"605336","mc":"帅丰电器","jys":"sh"},{"dm":"301332","mc":"德尔玛","jys":"sz"},{"dm":"301121","mc":"紫建电子","jys":"sz"},{"dm":"600838","mc":"上海九百","jys":"sh"},{"dm":"600703","mc":"三安光电","jys":"sh"},{"dm":"600370","mc":"三房巷","jys":"sh"},{"dm":"003023","mc":"彩虹集团","jys":"sz"},{"dm":"603056","mc":"德邦股份","jys":"sh"},{"dm":"600897","mc":"厦门空港","jys":"sh"},{"dm":"603659","mc":"璞泰来","jys":"sh"},{"dm":"000553","mc":"安道麦A","jys":"sz"},{"dm":"603132","mc":"金徽股份","jys":"sh"},{"dm":"601963","mc":"重庆银行","jys":"sh"},{"dm":"301377","mc":"鼎泰高科","jys":"sz"},{"dm":"600713","mc":"南京医药","jys":"sh"},{"dm":"301168","mc":"通灵股份","jys":"sz"},{"dm":"600359","mc":"新农开发","jys":"sh"},{"dm":"600576","mc":"祥源文旅","jys":"sh"},{"dm":"002579","mc":"中京电子","jys":"sz"},{"dm":"600517","mc":"国网英大","jys":"sh"},{"dm":"605003","mc":"众望布艺","jys":"sh"},{"dm":"600697","mc":"欧亚集团","jys":"sh"},{"dm":"301227","mc":"森鹰窗业","jys":"sz"},{"dm":"002425","mc":"凯撒文化","jys":"sz"},{"dm":"301046","mc":"能辉科技","jys":"sz"},{"dm":"300080","mc":"易成新能","jys":"sz"},{"dm":"300501","mc":"海顺新材","jys":"sz"},{"dm":"000592","mc":"平潭发展","jys":"sz"},{"dm":"000560","mc":"我爱我家","jys":"sz"},{"dm":"000768","mc":"中航西飞","jys":"sz"},{"dm":"688396","mc":"华润微","jys":"sh"},{"dm":"301001","mc":"凯淳股份","jys":"sz"},{"dm":"001236","mc":"弘业期货","jys":"sz"},{"dm":"300851","mc":"交大思诺","jys":"sz"},{"dm":"001332","mc":"锡装股份","jys":"sz"},{"dm":"000967","mc":"盈峰环境","jys":"sz"},{"dm":"002265","mc":"建设工业","jys":"sz"},{"dm":"603982","mc":"泉峰汽车","jys":"sh"},{"dm":"300662","mc":"科锐国际","jys":"sz"},{"dm":"601615","mc":"明阳智能","jys":"sh"},{"dm":"603115","mc":"海星股份","jys":"sh"},{"dm":"600467","mc":"好当家","jys":"sh"},{"dm":"000519","mc":"中兵红箭","jys":"sz"},{"dm":"600628","mc":"新世界","jys":"sh"},{"dm":"601128","mc":"常熟银行","jys":"sh"},{"dm":"603995","mc":"甬金股份","jys":"sh"},{"dm":"688312","mc":"燕麦科技","jys":"sh"},{"dm":"600905","mc":"三峡能源","jys":"sh"},{"dm":"688208","mc":"道通科技","jys":"sh"},{"dm":"002651","mc":"利君股份","jys":"sz"},{"dm":"002545","mc":"东方铁塔","jys":"sz"},{"dm":"002930","mc":"宏川智慧","jys":"sz"},{"dm":"002965","mc":"祥鑫科技","jys":"sz"},{"dm":"688247","mc":"宣泰医药","jys":"sh"},{"dm":"603077","mc":"和邦生物","jys":"sh"},{"dm":"002468","mc":"申通快递","jys":"sz"},{"dm":"000607","mc":"华媒控股","jys":"sz"},{"dm":"600150","mc":"中国船舶","jys":"sh"},{"dm":"300666","mc":"江丰电子","jys":"sz"},{"dm":"600597","mc":"光明乳业","jys":"sh"},{"dm":"601038","mc":"一拖股份","jys":"sh"},{"dm":"600178","mc":"东安动力","jys":"sh"},{"dm":"603909","mc":"建发合诚","jys":"sh"},{"dm":"603506","mc":"南都物业","jys":"sh"},{"dm":"002319","mc":"乐通股份","jys":"sz"},{"dm":"601908","mc":"京运通","jys":"sh"},{"dm":"603787","mc":"新日股份","jys":"sh"},{"dm":"603678","mc":"火炬电子","jys":"sh"},{"dm":"300257","mc":"开山股份","jys":"sz"},{"dm":"300176","mc":"派生科技","jys":"sz"},{"dm":"002304","mc":"洋河股份","jys":"sz"},{"dm":"002382","mc":"蓝帆医疗","jys":"sz"},{"dm":"688009","mc":"中国通号","jys":"sh"},{"dm":"300435","mc":"中泰股份","jys":"sz"},{"dm":"688118","mc":"普元信息","jys":"sh"},{"dm":"688789","mc":"宏华数科","jys":"sh"},{"dm":"300055","mc":"万邦达","jys":"sz"},{"dm":"002027","mc":"分众传媒","jys":"sz"},{"dm":"300039","mc":"上海凯宝","jys":"sz"},{"dm":"603087","mc":"甘李药业","jys":"sh"},{"dm":"603693","mc":"江苏新能","jys":"sh"},{"dm":"600000","mc":"浦发银行","jys":"sh"},{"dm":"601658","mc":"邮储银行","jys":"sh"},{"dm":"300197","mc":"节能铁汉","jys":"sz"},{"dm":"001313","mc":"粤海饲料","jys":"sz"},{"dm":"600755","mc":"厦门国贸","jys":"sh"},{"dm":"002707","mc":"众信旅游","jys":"sz"},{"dm":"300783","mc":"三只松鼠","jys":"sz"},{"dm":"000159","mc":"国际实业","jys":"sz"},{"dm":"601866","mc":"中远海发","jys":"sh"},{"dm":"000897","mc":"津滨发展","jys":"sz"},{"dm":"301022","mc":"海泰科","jys":"sz"},{"dm":"603217","mc":"元利科技","jys":"sh"},{"dm":"300864","mc":"南大环境","jys":"sz"},{"dm":"688285","mc":"高铁电气","jys":"sh"},{"dm":"603162","mc":"海通发展","jys":"sh"},{"dm":"601139","mc":"深圳燃气","jys":"sh"},{"dm":"300183","mc":"东软载波","jys":"sz"},{"dm":"300390","mc":"天华新能","jys":"sz"},{"dm":"002986","mc":"宇新股份","jys":"sz"},{"dm":"601579","mc":"会稽山","jys":"sh"},{"dm":"002198","mc":"嘉应制药","jys":"sz"},{"dm":"601118","mc":"海南橡胶","jys":"sh"},{"dm":"000006","mc":"深振业A","jys":"sz"},{"dm":"301101","mc":"明月镜片","jys":"sz"},{"dm":"001360","mc":"南矿集团","jys":"sz"},{"dm":"601222","mc":"林洋能源","jys":"sh"},{"dm":"002865","mc":"钧达股份","jys":"sz"},{"dm":"603378","mc":"亚士创能","jys":"sh"},{"dm":"002183","mc":"怡 亚 通","jys":"sz"},{"dm":"605060","mc":"联德股份","jys":"sh"},{"dm":"600726","mc":"华电能源","jys":"sh"},{"dm":"603260","mc":"合盛硅业","jys":"sh"},{"dm":"300139","mc":"晓程科技","jys":"sz"},{"dm":"300773","mc":"拉卡拉","jys":"sz"},{"dm":"601868","mc":"中国能建","jys":"sh"},{"dm":"002887","mc":"绿茵生态","jys":"sz"},{"dm":"300298","mc":"三诺生物","jys":"sz"},{"dm":"002701","mc":"奥瑞金","jys":"sz"},{"dm":"301276","mc":"嘉曼服饰","jys":"sz"},{"dm":"688184","mc":"帕瓦股份","jys":"sh"},{"dm":"000886","mc":"海南高速","jys":"sz"},{"dm":"600079","mc":"人福医药","jys":"sh"},{"dm":"002016","mc":"世荣兆业","jys":"sz"},{"dm":"301152","mc":"天力锂能","jys":"sz"},{"dm":"300148","mc":"天舟文化","jys":"sz"},{"dm":"600004","mc":"白云机场","jys":"sh"},{"dm":"003009","mc":"中天火箭","jys":"sz"},{"dm":"300206","mc":"理邦仪器","jys":"sz"},{"dm":"300086","mc":"康芝药业","jys":"sz"},{"dm":"002223","mc":"鱼跃医疗","jys":"sz"},{"dm":"300450","mc":"先导智能","jys":"sz"},{"dm":"300682","mc":"朗新科技","jys":"sz"},{"dm":"601952","mc":"苏垦农发","jys":"sh"},{"dm":"605567","mc":"春雪食品","jys":"sh"},{"dm":"600717","mc":"天津港","jys":"sh"},{"dm":"002623","mc":"亚玛顿","jys":"sz"},{"dm":"603309","mc":"维力医疗","jys":"sh"},{"dm":"301122","mc":"采纳股份","jys":"sz"},{"dm":"600556","mc":"天下秀","jys":"sh"},{"dm":"002686","mc":"亿利达","jys":"sz"},{"dm":"600992","mc":"贵绳股份","jys":"sh"},{"dm":"301511","mc":"德福科技","jys":"sz"},{"dm":"002466","mc":"天齐锂业","jys":"sz"},{"dm":"002921","mc":"联诚精密","jys":"sz"},{"dm":"600598","mc":"北大荒","jys":"sh"},{"dm":"002691","mc":"冀凯股份","jys":"sz"},{"dm":"600696","mc":"岩石股份","jys":"sh"},{"dm":"301026","mc":"浩通科技","jys":"sz"},{"dm":"002782","mc":"可立克","jys":"sz"},{"dm":"605136","mc":"丽人丽妆","jys":"sh"},{"dm":"002734","mc":"利民股份","jys":"sz"},{"dm":"002570","mc":"贝因美","jys":"sz"},{"dm":"002669","mc":"康达新材","jys":"sz"},{"dm":"002631","mc":"德尔未来","jys":"sz"},{"dm":"601688","mc":"华泰证券","jys":"sh"},{"dm":"601020","mc":"华钰矿业","jys":"sh"},{"dm":"300616","mc":"尚品宅配","jys":"sz"},{"dm":"688669","mc":"聚石化学","jys":"sh"},{"dm":"600419","mc":"天润乳业","jys":"sh"},{"dm":"002873","mc":"新天药业","jys":"sz"},{"dm":"002209","mc":"达 意 隆","jys":"sz"},{"dm":"301333","mc":"诺思格","jys":"sz"},{"dm":"601992","mc":"金隅集团","jys":"sh"},{"dm":"002886","mc":"沃特股份","jys":"sz"},{"dm":"688178","mc":"万德斯","jys":"sh"},{"dm":"300345","mc":"华民股份","jys":"sz"},{"dm":"002898","mc":"赛隆药业","jys":"sz"},{"dm":"688100","mc":"威胜信息","jys":"sh"},{"dm":"000581","mc":"威孚高科","jys":"sz"},{"dm":"300530","mc":"领湃科技","jys":"sz"},{"dm":"301257","mc":"普蕊斯","jys":"sz"},{"dm":"300446","mc":"乐凯新材","jys":"sz"},{"dm":"002563","mc":"森马服饰","jys":"sz"},{"dm":"600774","mc":"汉商集团","jys":"sh"},{"dm":"603990","mc":"麦迪科技","jys":"sh"},{"dm":"601800","mc":"中国交建","jys":"sh"},{"dm":"603223","mc":"恒通股份","jys":"sh"},{"dm":"301118","mc":"恒光股份","jys":"sz"},{"dm":"688067","mc":"爱威科技","jys":"sh"},{"dm":"688233","mc":"神工股份","jys":"sh"},{"dm":"601989","mc":"中国重工","jys":"sh"},{"dm":"600380","mc":"健康元","jys":"sh"},{"dm":"000976","mc":"ST华铁","jys":"sz"},{"dm":"301370","mc":"国科恒泰","jys":"sz"},{"dm":"000029","mc":"深深房A","jys":"sz"},{"dm":"603877","mc":"太平鸟","jys":"sh"},{"dm":"600581","mc":"八一钢铁","jys":"sh"},{"dm":"002422","mc":"科伦药业","jys":"sz"},{"dm":"603200","mc":"上海洗霸","jys":"sh"},{"dm":"600328","mc":"中盐化工","jys":"sh"},{"dm":"301222","mc":"浙江恒威","jys":"sz"},{"dm":"600859","mc":"王府井","jys":"sh"},{"dm":"600038","mc":"中直股份","jys":"sh"},{"dm":"300006","mc":"莱美药业","jys":"sz"},{"dm":"603135","mc":"中重科技","jys":"sh"},{"dm":"002683","mc":"广东宏大","jys":"sz"},{"dm":"300890","mc":"翔丰华","jys":"sz"},{"dm":"002317","mc":"众生药业","jys":"sz"},{"dm":"002531","mc":"天顺风能","jys":"sz"},{"dm":"600527","mc":"江南高纤","jys":"sh"},{"dm":"688646","mc":"逸飞激光","jys":"sh"},{"dm":"300278","mc":"华昌达","jys":"sz"},{"dm":"600812","mc":"华北制药","jys":"sh"},{"dm":"002045","mc":"国光电器","jys":"sz"},{"dm":"600596","mc":"新安股份","jys":"sh"},{"dm":"300013","mc":"新宁物流","jys":"sz"},{"dm":"688613","mc":"奥精医疗","jys":"sh"},{"dm":"600665","mc":"天地源","jys":"sh"},{"dm":"002075","mc":"沙钢股份","jys":"sz"},{"dm":"002714","mc":"牧原股份","jys":"sz"},{"dm":"601137","mc":"博威合金","jys":"sh"},{"dm":"600936","mc":"广西广电","jys":"sh"},{"dm":"600559","mc":"老白干酒","jys":"sh"},{"dm":"600167","mc":"联美控股","jys":"sh"},{"dm":"688552","mc":"航天南湖","jys":"sh"},{"dm":"301219","mc":"腾远钴业","jys":"sz"},{"dm":"000062","mc":"深圳华强","jys":"sz"},{"dm":"688388","mc":"嘉元科技","jys":"sh"},{"dm":"002043","mc":"兔 宝 宝","jys":"sz"},{"dm":"002210","mc":"飞马国际","jys":"sz"},{"dm":"605116","mc":"奥锐特","jys":"sh"},{"dm":"600391","mc":"航发科技","jys":"sh"},{"dm":"601919","mc":"中远海控","jys":"sh"},{"dm":"688266","mc":"泽璟制药-U","jys":"sh"},{"dm":"600871","mc":"石化油服","jys":"sh"},{"dm":"300421","mc":"力星股份","jys":"sz"},{"dm":"301211","mc":"亨迪药业","jys":"sz"},{"dm":"002765","mc":"蓝黛科技","jys":"sz"},{"dm":"600874","mc":"创业环保","jys":"sh"},{"dm":"600340","mc":"华夏幸福","jys":"sh"},{"dm":"603606","mc":"东方电缆","jys":"sh"},{"dm":"688128","mc":"中国电研","jys":"sh"},{"dm":"300335","mc":"迪森股份","jys":"sz"},{"dm":"002663","mc":"普邦股份","jys":"sz"},{"dm":"601166","mc":"兴业银行","jys":"sh"},{"dm":"688690","mc":"纳微科技","jys":"sh"},{"dm":"600409","mc":"三友化工","jys":"sh"},{"dm":"600803","mc":"新奥股份","jys":"sh"},{"dm":"002790","mc":"瑞尔特","jys":"sz"},{"dm":"600742","mc":"一汽富维","jys":"sh"},{"dm":"300778","mc":"新城市","jys":"sz"},{"dm":"601968","mc":"宝钢包装","jys":"sh"},{"dm":"600518","mc":"ST康美","jys":"sh"},{"dm":"600130","mc":"波导股份","jys":"sh"},{"dm":"300328","mc":"宜安科技","jys":"sz"},{"dm":"300301","mc":"*ST长方","jys":"sz"},{"dm":"688268","mc":"华特气体","jys":"sh"},{"dm":"600479","mc":"千金药业","jys":"sh"},{"dm":"000669","mc":"ST金鸿","jys":"sz"},{"dm":"600629","mc":"华建集团","jys":"sh"},{"dm":"000582","mc":"北部湾港","jys":"sz"},{"dm":"688186","mc":"广大特材","jys":"sh"},{"dm":"000869","mc":"张 裕A","jys":"sz"},{"dm":"300697","mc":"电工合金","jys":"sz"},{"dm":"600835","mc":"上海机电","jys":"sh"},{"dm":"300373","mc":"扬杰科技","jys":"sz"},{"dm":"300147","mc":"香雪制药","jys":"sz"},{"dm":"600858","mc":"银座股份","jys":"sh"},{"dm":"600373","mc":"中文传媒","jys":"sh"},{"dm":"600478","mc":"科力远","jys":"sh"},{"dm":"300869","mc":"康泰医学","jys":"sz"},{"dm":"300244","mc":"迪安诊断","jys":"sz"},{"dm":"688136","mc":"科兴制药","jys":"sh"},{"dm":"300770","mc":"新媒股份","jys":"sz"},{"dm":"300100","mc":"双林股份","jys":"sz"},{"dm":"600165","mc":"宁科生物","jys":"sh"},{"dm":"301429","mc":"森泰股份","jys":"sz"},{"dm":"001203","mc":"大中矿业","jys":"sz"},{"dm":"301206","mc":"三元生物","jys":"sz"},{"dm":"603298","mc":"杭叉集团","jys":"sh"},{"dm":"002329","mc":"皇氏集团","jys":"sz"},{"dm":"688026","mc":"洁特生物","jys":"sh"},{"dm":"000048","mc":"京基智农","jys":"sz"},{"dm":"002846","mc":"英联股份","jys":"sz"},{"dm":"600280","mc":"中央商场","jys":"sh"},{"dm":"002269","mc":"美邦服饰","jys":"sz"},{"dm":"300782","mc":"卓胜微","jys":"sz"},{"dm":"300837","mc":"浙矿股份","jys":"sz"},{"dm":"001368","mc":"通达创智","jys":"sz"},{"dm":"002179","mc":"中航光电","jys":"sz"},{"dm":"002550","mc":"千红制药","jys":"sz"},{"dm":"601021","mc":"春秋航空","jys":"sh"},{"dm":"003033","mc":"征和工业","jys":"sz"},{"dm":"688508","mc":"芯朋微","jys":"sh"},{"dm":"002780","mc":"三夫户外","jys":"sz"},{"dm":"002687","mc":"乔治白","jys":"sz"},{"dm":"605009","mc":"豪悦护理","jys":"sh"},{"dm":"002742","mc":"ST三圣","jys":"sz"},{"dm":"002051","mc":"中工国际","jys":"sz"},{"dm":"601611","mc":"中国核建","jys":"sh"},{"dm":"603299","mc":"苏盐井神","jys":"sh"},{"dm":"600306","mc":"*ST商城","jys":"sh"},{"dm":"688317","mc":"之江生物","jys":"sh"},{"dm":"600106","mc":"重庆路桥","jys":"sh"},{"dm":"002039","mc":"黔源电力","jys":"sz"},{"dm":"301296","mc":"新巨丰","jys":"sz"},{"dm":"600021","mc":"上海电力","jys":"sh"},{"dm":"688148","mc":"芳源股份","jys":"sh"},{"dm":"603031","mc":"安孚科技","jys":"sh"},{"dm":"000950","mc":"重药控股","jys":"sz"},{"dm":"001338","mc":"永顺泰","jys":"sz"},{"dm":"600501","mc":"航天晨光","jys":"sh"},{"dm":"002905","mc":"金逸影视","jys":"sz"},{"dm":"301017","mc":"漱玉平民","jys":"sz"},{"dm":"300896","mc":"爱美客","jys":"sz"},{"dm":"605338","mc":"巴比食品","jys":"sh"},{"dm":"603337","mc":"杰克股份","jys":"sh"},{"dm":"000031","mc":"大悦城","jys":"sz"},{"dm":"603256","mc":"宏和科技","jys":"sh"},{"dm":"300549","mc":"优德精密","jys":"sz"},{"dm":"002352","mc":"顺丰控股","jys":"sz"},{"dm":"600967","mc":"内蒙一机","jys":"sh"},{"dm":"688630","mc":"芯碁微装","jys":"sh"},{"dm":"601997","mc":"贵阳银行","jys":"sh"},{"dm":"600827","mc":"百联股份","jys":"sh"},{"dm":"600151","mc":"航天机电","jys":"sh"},{"dm":"300443","mc":"金雷股份","jys":"sz"},{"dm":"601330","mc":"绿色动力","jys":"sh"},{"dm":"600320","mc":"振华重工","jys":"sh"},{"dm":"688368","mc":"晶丰明源","jys":"sh"},{"dm":"600787","mc":"中储股份","jys":"sh"},{"dm":"300861","mc":"美畅股份","jys":"sz"},{"dm":"600973","mc":"宝胜股份","jys":"sh"},{"dm":"301033","mc":"迈普医学","jys":"sz"},{"dm":"000007","mc":"*ST全新","jys":"sz"},{"dm":"300607","mc":"拓斯达","jys":"sz"},{"dm":"000799","mc":"酒鬼酒","jys":"sz"},{"dm":"600744","mc":"华银电力","jys":"sh"},{"dm":"002412","mc":"汉森制药","jys":"sz"},{"dm":"000788","mc":"北大医药","jys":"sz"},{"dm":"000792","mc":"盐湖股份","jys":"sz"},{"dm":"600875","mc":"东方电气","jys":"sh"},{"dm":"003020","mc":"立方制药","jys":"sz"},{"dm":"600603","mc":"广汇物流","jys":"sh"},{"dm":"300775","mc":"三角防务","jys":"sz"},{"dm":"300457","mc":"赢合科技","jys":"sz"},{"dm":"002052","mc":"ST同洲","jys":"sz"},{"dm":"600361","mc":"创新新材","jys":"sh"},{"dm":"002244","mc":"滨江集团","jys":"sz"},{"dm":"002732","mc":"燕塘乳业","jys":"sz"},{"dm":"300575","mc":"中旗股份","jys":"sz"},{"dm":"001309","mc":"德明利","jys":"sz"},{"dm":"603032","mc":"德新科技","jys":"sh"},{"dm":"301515","mc":"港通医疗","jys":"sz"},{"dm":"002968","mc":"新大正","jys":"sz"},{"dm":"002010","mc":"传化智联","jys":"sz"},{"dm":"601231","mc":"环旭电子","jys":"sh"},{"dm":"600104","mc":"上汽集团","jys":"sh"},{"dm":"603316","mc":"诚邦股份","jys":"sh"},{"dm":"601888","mc":"中国中免","jys":"sh"},{"dm":"688176","mc":"亚虹医药-U","jys":"sh"},{"dm":"600496","mc":"精工钢构","jys":"sh"},{"dm":"300815","mc":"玉禾田","jys":"sz"},{"dm":"688625","mc":"呈和科技","jys":"sh"},{"dm":"600097","mc":"开创国际","jys":"sh"},{"dm":"301047","mc":"义翘神州","jys":"sz"},{"dm":"300239","mc":"东宝生物","jys":"sz"},{"dm":"002149","mc":"西部材料","jys":"sz"},{"dm":"600010","mc":"包钢股份","jys":"sh"},{"dm":"600027","mc":"华电国际","jys":"sh"},{"dm":"600685","mc":"中船防务","jys":"sh"},{"dm":"688084","mc":"晶品特装","jys":"sh"},{"dm":"603296","mc":"华勤技术","jys":"sh"},{"dm":"600655","mc":"豫园股份","jys":"sh"},{"dm":"002948","mc":"青岛银行","jys":"sz"},{"dm":"000656","mc":"金科股份","jys":"sz"},{"dm":"002167","mc":"东方锆业","jys":"sz"},{"dm":"300848","mc":"美瑞新材","jys":"sz"},{"dm":"300981","mc":"中红医疗","jys":"sz"},{"dm":"600132","mc":"重庆啤酒","jys":"sh"},{"dm":"603665","mc":"康隆达","jys":"sh"},{"dm":"688179","mc":"阿拉丁","jys":"sh"},{"dm":"605389","mc":"长龄液压","jys":"sh"},{"dm":"603321","mc":"梅轮电梯","jys":"sh"},{"dm":"300428","mc":"立中集团","jys":"sz"},{"dm":"603023","mc":"威帝股份","jys":"sh"},{"dm":"000690","mc":"宝新能源","jys":"sz"},{"dm":"002187","mc":"广百股份","jys":"sz"},{"dm":"600639","mc":"浦东金桥","jys":"sh"},{"dm":"600565","mc":"迪马股份","jys":"sh"},{"dm":"601965","mc":"中国汽研","jys":"sh"},{"dm":"603386","mc":"骏亚科技","jys":"sh"},{"dm":"600358","mc":"国旅联合","jys":"sh"},{"dm":"300595","mc":"欧普康视","jys":"sz"},{"dm":"688196","mc":"卓越新能","jys":"sh"},{"dm":"600616","mc":"金枫酒业","jys":"sh"},{"dm":"301057","mc":"汇隆新材","jys":"sz"},{"dm":"000819","mc":"岳阳兴长","jys":"sz"},{"dm":"300214","mc":"日科化学","jys":"sz"},{"dm":"601016","mc":"节能风电","jys":"sh"},{"dm":"600216","mc":"浙江医药","jys":"sh"},{"dm":"600293","mc":"三峡新材","jys":"sh"},{"dm":"603883","mc":"老百姓","jys":"sh"},{"dm":"603708","mc":"家家悦","jys":"sh"},{"dm":"601689","mc":"拓普集团","jys":"sh"},{"dm":"600300","mc":"维维股份","jys":"sh"},{"dm":"001979","mc":"招商蛇口","jys":"sz"},{"dm":"301111","mc":"粤万年青","jys":"sz"},{"dm":"301050","mc":"雷电微力","jys":"sz"},{"dm":"002966","mc":"苏州银行","jys":"sz"},{"dm":"600063","mc":"皖维高新","jys":"sh"},{"dm":"600117","mc":"*ST西钢","jys":"sh"},{"dm":"002020","mc":"京新药业","jys":"sz"},{"dm":"688072","mc":"拓荆科技","jys":"sh"},{"dm":"002028","mc":"思源电气","jys":"sz"},{"dm":"300619","mc":"金银河","jys":"sz"},{"dm":"600557","mc":"康缘药业","jys":"sh"},{"dm":"601179","mc":"中国西电","jys":"sh"},{"dm":"600166","mc":"福田汽车","jys":"sh"},{"dm":"000928","mc":"中钢国际","jys":"sz"},{"dm":"301071","mc":"力量钻石","jys":"sz"},{"dm":"603385","mc":"惠达卫浴","jys":"sh"},{"dm":"605100","mc":"华丰股份","jys":"sh"},{"dm":"002204","mc":"大连重工","jys":"sz"},{"dm":"000966","mc":"长源电力","jys":"sz"},{"dm":"300453","mc":"三鑫医疗","jys":"sz"},{"dm":"300642","mc":"透景生命","jys":"sz"},{"dm":"300776","mc":"帝尔激光","jys":"sz"},{"dm":"600110","mc":"诺德股份","jys":"sh"},{"dm":"603099","mc":"长白山","jys":"sh"},{"dm":"000767","mc":"晋控电力","jys":"sz"},{"dm":"300146","mc":"汤臣倍健","jys":"sz"},{"dm":"688518","mc":"联赢激光","jys":"sh"},{"dm":"000426","mc":"兴业银锡","jys":"sz"},{"dm":"002612","mc":"朗姿股份","jys":"sz"},{"dm":"301200","mc":"大族数控","jys":"sz"},{"dm":"301277","mc":"新天地","jys":"sz"},{"dm":"688550","mc":"瑞联新材","jys":"sh"},{"dm":"600190","mc":"锦州港","jys":"sh"},{"dm":"002091","mc":"江苏国泰","jys":"sz"},{"dm":"603661","mc":"恒林股份","jys":"sh"},{"dm":"301221","mc":"光庭信息","jys":"sz"},{"dm":"688278","mc":"特宝生物","jys":"sh"},{"dm":"002816","mc":"*ST和科","jys":"sz"},{"dm":"603515","mc":"欧普照明","jys":"sh"},{"dm":"000630","mc":"铜陵有色","jys":"sz"},{"dm":"300378","mc":"鼎捷软件","jys":"sz"},{"dm":"002372","mc":"伟星新材","jys":"sz"},{"dm":"002584","mc":"西陇科学","jys":"sz"},{"dm":"000961","mc":"中南建设","jys":"sz"},{"dm":"600955","mc":"维远股份","jys":"sh"},{"dm":"605398","mc":"新炬网络","jys":"sh"},{"dm":"601298","mc":"青岛港","jys":"sh"},{"dm":"002733","mc":"雄韬股份","jys":"sz"},{"dm":"002501","mc":"利源股份","jys":"sz"},{"dm":"001330","mc":"博纳影业","jys":"sz"},{"dm":"605077","mc":"华康股份","jys":"sh"},{"dm":"300149","mc":"睿智医药","jys":"sz"},{"dm":"600578","mc":"京能电力","jys":"sh"},{"dm":"300676","mc":"华大基因","jys":"sz"},{"dm":"000789","mc":"万年青","jys":"sz"},{"dm":"601678","mc":"滨化股份","jys":"sh"},{"dm":"300797","mc":"钢研纳克","jys":"sz"},{"dm":"603300","mc":"华铁应急","jys":"sh"},{"dm":"603868","mc":"飞科电器","jys":"sh"},{"dm":"002407","mc":"多氟多","jys":"sz"},{"dm":"603519","mc":"立霸股份","jys":"sh"},{"dm":"300729","mc":"乐歌股份","jys":"sz"},{"dm":"300669","mc":"沪宁股份","jys":"sz"},{"dm":"000720","mc":"新能泰山","jys":"sz"},{"dm":"688739","mc":"成大生物","jys":"sh"},{"dm":"301073","mc":"君亭酒店","jys":"sz"},{"dm":"002003","mc":"伟星股份","jys":"sz"},{"dm":"601600","mc":"中国铝业","jys":"sh"},{"dm":"002595","mc":"豪迈科技","jys":"sz"},{"dm":"002950","mc":"奥美医疗","jys":"sz"},{"dm":"605090","mc":"九丰能源","jys":"sh"},{"dm":"600594","mc":"益佰制药","jys":"sh"},{"dm":"600389","mc":"江山股份","jys":"sh"},{"dm":"688363","mc":"华熙生物","jys":"sh"},{"dm":"002423","mc":"中粮资本","jys":"sz"},{"dm":"300821","mc":"东岳硅材","jys":"sz"},{"dm":"600316","mc":"洪都航空","jys":"sh"},{"dm":"000016","mc":"深康佳A","jys":"sz"},{"dm":"600009","mc":"上海机场","jys":"sh"},{"dm":"300636","mc":"同和药业","jys":"sz"},{"dm":"600031","mc":"三一重工","jys":"sh"},{"dm":"603533","mc":"掌阅科技","jys":"sh"},{"dm":"002102","mc":"冠福股份","jys":"sz"},{"dm":"601226","mc":"华电重工","jys":"sh"},{"dm":"301095","mc":"广立微","jys":"sz"},{"dm":"003031","mc":"中瓷电子","jys":"sz"},{"dm":"600248","mc":"陕建股份","jys":"sh"},{"dm":"603978","mc":"深圳新星","jys":"sh"},{"dm":"000425","mc":"徐工机械","jys":"sz"},{"dm":"300347","mc":"泰格医药","jys":"sz"},{"dm":"688722","mc":"同益中","jys":"sh"},{"dm":"603613","mc":"国联股份","jys":"sh"},{"dm":"300463","mc":"迈克生物","jys":"sz"},{"dm":"000401","mc":"冀东水泥","jys":"sz"},{"dm":"002524","mc":"光正眼科","jys":"sz"},{"dm":"002355","mc":"兴民智通","jys":"sz"},{"dm":"600085","mc":"同仁堂","jys":"sh"},{"dm":"002829","mc":"星网宇达","jys":"sz"},{"dm":"603878","mc":"武进不锈","jys":"sh"},{"dm":"301258","mc":"富士莱","jys":"sz"},{"dm":"002551","mc":"尚荣医疗","jys":"sz"},{"dm":"301103","mc":"何氏眼科","jys":"sz"},{"dm":"300568","mc":"星源材质","jys":"sz"},{"dm":"603199","mc":"九华旅游","jys":"sh"},{"dm":"002626","mc":"金达威","jys":"sz"},{"dm":"300842","mc":"帝科股份","jys":"sz"},{"dm":"600269","mc":"赣粤高速","jys":"sh"},{"dm":"300652","mc":"雷迪克","jys":"sz"},{"dm":"002767","mc":"先锋电子","jys":"sz"},{"dm":"603719","mc":"良品铺子","jys":"sh"},{"dm":"603330","mc":"天洋新材","jys":"sh"},{"dm":"600475","mc":"华光环能","jys":"sh"},{"dm":"000598","mc":"兴蓉环境","jys":"sz"},{"dm":"600008","mc":"首创环保","jys":"sh"},{"dm":"600919","mc":"江苏银行","jys":"sh"},{"dm":"000930","mc":"中粮科技","jys":"sz"},{"dm":"002080","mc":"中材科技","jys":"sz"},{"dm":"688132","mc":"邦彦技术","jys":"sh"},{"dm":"002081","mc":"金 螳 螂","jys":"sz"},{"dm":"603237","mc":"五芳斋","jys":"sh"},{"dm":"000885","mc":"城发环境","jys":"sz"},{"dm":"002271","mc":"东方雨虹","jys":"sz"},{"dm":"605599","mc":"菜百股份","jys":"sh"},{"dm":"600405","mc":"动力源","jys":"sh"},{"dm":"600187","mc":"国中水务","jys":"sh"},{"dm":"002716","mc":"金贵银业","jys":"sz"},{"dm":"688548","mc":"广钢气体","jys":"sh"},{"dm":"002779","mc":"中坚科技","jys":"sz"},{"dm":"600785","mc":"新华百货","jys":"sh"},{"dm":"002419","mc":"天虹股份","jys":"sz"},{"dm":"603889","mc":"新澳股份","jys":"sh"},{"dm":"002033","mc":"丽江股份","jys":"sz"},{"dm":"000597","mc":"东北制药","jys":"sz"},{"dm":"600201","mc":"生物股份","jys":"sh"},{"dm":"301149","mc":"隆华新材","jys":"sz"},{"dm":"600808","mc":"马钢股份","jys":"sh"},{"dm":"300702","mc":"天宇股份","jys":"sz"},{"dm":"300003","mc":"乐普医疗","jys":"sz"},{"dm":"000089","mc":"深圳机场","jys":"sz"},{"dm":"600765","mc":"中航重机","jys":"sh"},{"dm":"002275","mc":"桂林三金","jys":"sz"},{"dm":"002084","mc":"海鸥住工","jys":"sz"},{"dm":"600138","mc":"中青旅","jys":"sh"},{"dm":"688399","mc":"硕世生物","jys":"sh"},{"dm":"600866","mc":"星湖科技","jys":"sh"},{"dm":"000778","mc":"新兴铸管","jys":"sz"},{"dm":"603993","mc":"洛阳钼业","jys":"sh"},{"dm":"600351","mc":"亚宝药业","jys":"sh"},{"dm":"603026","mc":"胜华新材","jys":"sh"},{"dm":"603822","mc":"嘉澳环保","jys":"sh"},{"dm":"600295","mc":"鄂尔多斯","jys":"sh"},{"dm":"300558","mc":"贝达药业","jys":"sz"},{"dm":"603165","mc":"荣晟环保","jys":"sh"},{"dm":"002896","mc":"中大力德","jys":"sz"},{"dm":"300059","mc":"东方财富","jys":"sz"},{"dm":"600226","mc":"瀚叶股份","jys":"sh"},{"dm":"002526","mc":"山东矿机","jys":"sz"},{"dm":"002366","mc":"融发核电","jys":"sz"},{"dm":"603808","mc":"歌力思","jys":"sh"},{"dm":"601669","mc":"中国电建","jys":"sh"},{"dm":"002225","mc":"濮耐股份","jys":"sz"},{"dm":"002791","mc":"坚朗五金","jys":"sz"},{"dm":"300339","mc":"润和软件","jys":"sz"},{"dm":"300103","mc":"达刚控股","jys":"sz"},{"dm":"600702","mc":"舍得酒业","jys":"sh"},{"dm":"603611","mc":"诺力股份","jys":"sh"},{"dm":"600759","mc":"*ST洲际","jys":"sh"},{"dm":"002654","mc":"万润科技","jys":"sz"},{"dm":"002326","mc":"永太科技","jys":"sz"},{"dm":"000822","mc":"山东海化","jys":"sz"},{"dm":"002694","mc":"顾地科技","jys":"sz"},{"dm":"600662","mc":"外服控股","jys":"sh"},{"dm":"605123","mc":"派克新材","jys":"sh"},{"dm":"000825","mc":"太钢不锈","jys":"sz"},{"dm":"605167","mc":"利柏特","jys":"sh"},{"dm":"603778","mc":"乾景园林","jys":"sh"},{"dm":"601991","mc":"大唐发电","jys":"sh"},{"dm":"603179","mc":"新泉股份","jys":"sh"},{"dm":"002345","mc":"潮宏基","jys":"sz"},{"dm":"600396","mc":"*ST金山","jys":"sh"},{"dm":"002739","mc":"万达电影","jys":"sz"},{"dm":"300534","mc":"陇神戎发","jys":"sz"},{"dm":"300715","mc":"凯伦股份","jys":"sz"},{"dm":"600322","mc":"津投城开","jys":"sh"},{"dm":"300433","mc":"蓝思科技","jys":"sz"},{"dm":"301005","mc":"超捷股份","jys":"sz"},{"dm":"002293","mc":"罗莱生活","jys":"sz"},{"dm":"603599","mc":"广信股份","jys":"sh"},{"dm":"600509","mc":"天富能源","jys":"sh"},{"dm":"300158","mc":"振东制药","jys":"sz"},{"dm":"300918","mc":"南山智尚","jys":"sz"},{"dm":"000729","mc":"燕京啤酒","jys":"sz"},{"dm":"000917","mc":"电广传媒","jys":"sz"},{"dm":"002900","mc":"哈三联","jys":"sz"},{"dm":"301456","mc":"盘古智能","jys":"sz"},{"dm":"601566","mc":"九牧王","jys":"sh"},{"dm":"002594","mc":"比亚迪","jys":"sz"},{"dm":"600376","mc":"首开股份","jys":"sh"},{"dm":"600690","mc":"海尔智家","jys":"sh"},{"dm":"603116","mc":"红蜻蜓","jys":"sh"},{"dm":"603209","mc":"兴通股份","jys":"sh"},{"dm":"600748","mc":"上实发展","jys":"sh"},{"dm":"301090","mc":"华润材料","jys":"sz"},{"dm":"301065","mc":"本立科技","jys":"sz"},{"dm":"600500","mc":"中化国际","jys":"sh"},{"dm":"603279","mc":"景津装备","jys":"sh"},{"dm":"000027","mc":"深圳能源","jys":"sz"},{"dm":"002053","mc":"云南能投","jys":"sz"},{"dm":"603301","mc":"振德医疗","jys":"sh"},{"dm":"002973","mc":"侨银股份","jys":"sz"},{"dm":"603799","mc":"华友钴业","jys":"sh"},{"dm":"600521","mc":"华海药业","jys":"sh"},{"dm":"002420","mc":"毅昌科技","jys":"sz"},{"dm":"000931","mc":"中 关 村","jys":"sz"},{"dm":"000546","mc":"金圆股份","jys":"sz"},{"dm":"300294","mc":"博雅生物","jys":"sz"},{"dm":"601778","mc":"晶科科技","jys":"sh"},{"dm":"000959","mc":"首钢股份","jys":"sz"},{"dm":"002535","mc":"林州重机","jys":"sz"},{"dm":"603095","mc":"越剑智能","jys":"sh"},{"dm":"603989","mc":"艾华集团","jys":"sh"},{"dm":"002967","mc":"广电计量","jys":"sz"},{"dm":"300405","mc":"科隆股份","jys":"sz"},{"dm":"000589","mc":"贵州轮胎","jys":"sz"},{"dm":"000410","mc":"沈阳机床","jys":"sz"},{"dm":"000915","mc":"华特达因","jys":"sz"},{"dm":"688087","mc":"英科再生","jys":"sh"},{"dm":"603833","mc":"欧派家居","jys":"sh"},{"dm":"002289","mc":"ST宇顺","jys":"sz"},{"dm":"603128","mc":"华贸物流","jys":"sh"},{"dm":"600502","mc":"安徽建工","jys":"sh"},{"dm":"603811","mc":"诚意药业","jys":"sh"},{"dm":"603235","mc":"天新药业","jys":"sh"},{"dm":"301116","mc":"益客食品","jys":"sz"},{"dm":"002150","mc":"通润装备","jys":"sz"},{"dm":"600029","mc":"南方航空","jys":"sh"},{"dm":"002821","mc":"凯莱英","jys":"sz"},{"dm":"002035","mc":"华帝股份","jys":"sz"},{"dm":"605299","mc":"舒华体育","jys":"sh"},{"dm":"603697","mc":"有友食品","jys":"sh"},{"dm":"002700","mc":"ST浩源","jys":"sz"},{"dm":"000596","mc":"古井贡酒","jys":"sz"},{"dm":"605080","mc":"浙江自然","jys":"sh"},{"dm":"000534","mc":"万泽股份","jys":"sz"},{"dm":"002413","mc":"雷科防务","jys":"sz"},{"dm":"002868","mc":"绿康生化","jys":"sz"},{"dm":"600926","mc":"杭州银行","jys":"sh"},{"dm":"002424","mc":"贵州百灵","jys":"sz"},{"dm":"600429","mc":"三元股份","jys":"sh"},{"dm":"600332","mc":"白云山","jys":"sh"},{"dm":"002245","mc":"蔚蓝锂芯","jys":"sz"},{"dm":"688393","mc":"安必平","jys":"sh"},{"dm":"000001","mc":"平安银行","jys":"sz"},{"dm":"603367","mc":"辰欣药业","jys":"sh"},{"dm":"600298","mc":"安琪酵母","jys":"sh"},{"dm":"600863","mc":"内蒙华电","jys":"sh"},{"dm":"002309","mc":"ST中利","jys":"sz"},{"dm":"600020","mc":"中原高速","jys":"sh"},{"dm":"002311","mc":"海大集团","jys":"sz"},{"dm":"688512","mc":"慧智微-U","jys":"sh"},{"dm":"000919","mc":"金陵药业","jys":"sz"},{"dm":"300725","mc":"药石科技","jys":"sz"},{"dm":"300622","mc":"博士眼镜","jys":"sz"},{"dm":"000623","mc":"吉林敖东","jys":"sz"},{"dm":"000875","mc":"吉电股份","jys":"sz"},{"dm":"000651","mc":"格力电器","jys":"sz"},{"dm":"600986","mc":"浙文互联","jys":"sh"},{"dm":"601117","mc":"中国化学","jys":"sh"},{"dm":"601512","mc":"中新集团","jys":"sh"},{"dm":"688533","mc":"上声电子","jys":"sh"},{"dm":"002676","mc":"顺威股份","jys":"sz"},{"dm":"000738","mc":"航发控制","jys":"sz"},{"dm":"600642","mc":"申能股份","jys":"sh"},{"dm":"603517","mc":"绝味食品","jys":"sh"},{"dm":"301008","mc":"宏昌科技","jys":"sz"},{"dm":"002131","mc":"利欧股份","jys":"sz"},{"dm":"000890","mc":"法尔胜","jys":"sz"},{"dm":"000591","mc":"太阳能","jys":"sz"},{"dm":"688033","mc":"天宜上佳","jys":"sh"},{"dm":"601717","mc":"郑煤机","jys":"sh"},{"dm":"600825","mc":"新华传媒","jys":"sh"},{"dm":"000055","mc":"方大集团","jys":"sz"},{"dm":"000952","mc":"广济药业","jys":"sz"},{"dm":"300267","mc":"尔康制药","jys":"sz"},{"dm":"688503","mc":"聚和材料","jys":"sh"},{"dm":"600869","mc":"远东股份","jys":"sh"},{"dm":"301301","mc":"川宁生物","jys":"sz"},{"dm":"600861","mc":"北京人力","jys":"sh"},{"dm":"300031","mc":"宝通科技","jys":"sz"},{"dm":"001258","mc":"立新能源","jys":"sz"},{"dm":"688410","mc":"山外山","jys":"sh"},{"dm":"002435","mc":"长江健康","jys":"sz"},{"dm":"601003","mc":"柳钢股份","jys":"sh"},{"dm":"002735","mc":"王子新材","jys":"sz"},{"dm":"000899","mc":"赣能股份","jys":"sz"},{"dm":"000910","mc":"大亚圣象","jys":"sz"},{"dm":"600694","mc":"大商股份","jys":"sh"},{"dm":"600231","mc":"凌钢股份","jys":"sh"},{"dm":"002445","mc":"中南文化","jys":"sz"},{"dm":"300978","mc":"东箭科技","jys":"sz"},{"dm":"002510","mc":"天汽模","jys":"sz"},{"dm":"603707","mc":"健友股份","jys":"sh"},{"dm":"600054","mc":"黄山旅游","jys":"sh"},{"dm":"603809","mc":"豪能股份","jys":"sh"},{"dm":"600416","mc":"湘电股份","jys":"sh"},{"dm":"002907","mc":"华森制药","jys":"sz"},{"dm":"603076","mc":"乐惠国际","jys":"sh"},{"dm":"301123","mc":"奕东电子","jys":"sz"},{"dm":"002258","mc":"利尔化学","jys":"sz"},{"dm":"688289","mc":"圣湘生物","jys":"sh"},{"dm":"301280","mc":"珠城科技","jys":"sz"},{"dm":"600595","mc":"中孚实业","jys":"sh"},{"dm":"603518","mc":"锦泓集团","jys":"sh"},{"dm":"002672","mc":"东江环保","jys":"sz"},{"dm":"603198","mc":"迎驾贡酒","jys":"sh"},{"dm":"002805","mc":"丰元股份","jys":"sz"},{"dm":"002483","mc":"润邦股份","jys":"sz"},{"dm":"002959","mc":"小熊电器","jys":"sz"},{"dm":"603979","mc":"金诚信","jys":"sh"},{"dm":"605507","mc":"国邦医药","jys":"sh"},{"dm":"603477","mc":"巨星农牧","jys":"sh"},{"dm":"603030","mc":"*ST全筑","jys":"sh"},{"dm":"603159","mc":"上海亚虹","jys":"sh"},{"dm":"002294","mc":"信立泰","jys":"sz"},{"dm":"600862","mc":"中航高科","jys":"sh"},{"dm":"000506","mc":"中润资源","jys":"sz"},{"dm":"601618","mc":"中国中冶","jys":"sh"},{"dm":"300073","mc":"当升科技","jys":"sz"},{"dm":"000709","mc":"河钢股份","jys":"sz"},{"dm":"000538","mc":"云南白药","jys":"sz"},{"dm":"688633","mc":"星球石墨","jys":"sh"},{"dm":"603308","mc":"应流股份","jys":"sh"},{"dm":"300930","mc":"屹通新材","jys":"sz"},{"dm":"002305","mc":"南国置业","jys":"sz"},{"dm":"300760","mc":"迈瑞医疗","jys":"sz"},{"dm":"300360","mc":"炬华科技","jys":"sz"},{"dm":"603551","mc":"奥普家居","jys":"sh"},{"dm":"002511","mc":"中顺洁柔","jys":"sz"},{"dm":"688551","mc":"科威尔","jys":"sh"},{"dm":"001207","mc":"联科科技","jys":"sz"},{"dm":"605081","mc":"太和水","jys":"sh"},{"dm":"603876","mc":"鼎胜新材","jys":"sh"},{"dm":"603777","mc":"来伊份","jys":"sh"},{"dm":"002371","mc":"北方华创","jys":"sz"},{"dm":"300712","mc":"永福股份","jys":"sz"},{"dm":"300467","mc":"迅游科技","jys":"sz"},{"dm":"600612","mc":"老凤祥","jys":"sh"},{"dm":"300401","mc":"花园生物","jys":"sz"},{"dm":"000524","mc":"岭南控股","jys":"sz"},{"dm":"600888","mc":"新疆众和","jys":"sh"},{"dm":"603897","mc":"长城科技","jys":"sh"},{"dm":"600720","mc":"祁连山","jys":"sh"},{"dm":"601187","mc":"厦门银行","jys":"sh"},{"dm":"000609","mc":"中迪投资","jys":"sz"},{"dm":"688049","mc":"炬芯科技","jys":"sh"},{"dm":"600600","mc":"青岛啤酒","jys":"sh"},{"dm":"603722","mc":"阿科力","jys":"sh"},{"dm":"000627","mc":"天茂集团","jys":"sz"},{"dm":"300653","mc":"正海生物","jys":"sz"},{"dm":"301308","mc":"江波龙","jys":"sz"},{"dm":"300755","mc":"华致酒行","jys":"sz"},{"dm":"600436","mc":"片仔癀","jys":"sh"},{"dm":"601686","mc":"友发集团","jys":"sh"},{"dm":"603836","mc":"海程邦达","jys":"sh"},{"dm":"002838","mc":"道恩股份","jys":"sz"},{"dm":"688336","mc":"三生国健","jys":"sh"},{"dm":"002690","mc":"美亚光电","jys":"sz"},{"dm":"603000","mc":"人民网","jys":"sh"},{"dm":"603718","mc":"海利生物","jys":"sh"},{"dm":"600585","mc":"海螺水泥","jys":"sh"},{"dm":"300240","mc":"飞力达","jys":"sz"},{"dm":"688126","mc":"沪硅产业","jys":"sh"},{"dm":"002356","mc":"赫美集团","jys":"sz"},{"dm":"001223","mc":"欧克科技","jys":"sz"},{"dm":"601065","mc":"江盐集团","jys":"sh"},{"dm":"300596","mc":"利安隆","jys":"sz"},{"dm":"688516","mc":"奥特维","jys":"sh"},{"dm":"002126","mc":"银轮股份","jys":"sz"},{"dm":"603267","mc":"鸿远电子","jys":"sh"},{"dm":"000028","mc":"国药一致","jys":"sz"},{"dm":"002192","mc":"融捷股份","jys":"sz"},{"dm":"301171","mc":"易点天下","jys":"sz"},{"dm":"688314","mc":"康拓医疗","jys":"sh"},{"dm":"600428","mc":"中远海特","jys":"sh"},{"dm":"301060","mc":"兰卫医学","jys":"sz"},{"dm":"603578","mc":"三星新材","jys":"sh"},{"dm":"601933","mc":"永辉超市","jys":"sh"},{"dm":"300276","mc":"三丰智能","jys":"sz"},{"dm":"600255","mc":"鑫科材料","jys":"sh"},{"dm":"688257","mc":"新锐股份","jys":"sh"},{"dm":"300286","mc":"安科瑞","jys":"sz"},{"dm":"000862","mc":"银星能源","jys":"sz"},{"dm":"601155","mc":"新城控股","jys":"sh"},{"dm":"688611","mc":"杭州柯林","jys":"sh"},{"dm":"002941","mc":"新疆交建","jys":"sz"},{"dm":"002472","mc":"双环传动","jys":"sz"},{"dm":"603315","mc":"福鞍股份","jys":"sh"},{"dm":"000861","mc":"海印股份","jys":"sz"},{"dm":"603035","mc":"常熟汽饰","jys":"sh"},{"dm":"600613","mc":"神奇制药","jys":"sh"},{"dm":"002582","mc":"好想你","jys":"sz"},{"dm":"000537","mc":"广宇发展","jys":"sz"},{"dm":"600779","mc":"水井坊","jys":"sh"},{"dm":"600528","mc":"中铁工业","jys":"sh"},{"dm":"300381","mc":"溢多利","jys":"sz"},{"dm":"002389","mc":"航天彩虹","jys":"sz"},{"dm":"002436","mc":"兴森科技","jys":"sz"},{"dm":"603588","mc":"高能环境","jys":"sh"},{"dm":"301102","mc":"兆讯传媒","jys":"sz"},{"dm":"600970","mc":"中材国际","jys":"sh"},{"dm":"600968","mc":"海油发展","jys":"sh"},{"dm":"000710","mc":"贝瑞基因","jys":"sz"},{"dm":"603638","mc":"艾迪精密","jys":"sh"},{"dm":"603589","mc":"口子窖","jys":"sh"},{"dm":"300668","mc":"杰恩设计","jys":"sz"},{"dm":"601111","mc":"中国国航","jys":"sh"},{"dm":"301393","mc":"昊帆生物","jys":"sz"},{"dm":"603567","mc":"珍宝岛","jys":"sh"},{"dm":"301520","mc":"万邦医药","jys":"sz"},{"dm":"601865","mc":"福莱特","jys":"sh"},{"dm":"300973","mc":"立高食品","jys":"sz"},{"dm":"300235","mc":"方直科技","jys":"sz"},{"dm":"301106","mc":"骏成科技","jys":"sz"},{"dm":"688379","mc":"华光新材","jys":"sh"},{"dm":"600153","mc":"建发股份","jys":"sh"},{"dm":"600297","mc":"广汇汽车","jys":"sh"},{"dm":"002146","mc":"荣盛发展","jys":"sz"},{"dm":"301024","mc":"霍普股份","jys":"sz"},{"dm":"002390","mc":"信邦制药","jys":"sz"},{"dm":"688222","mc":"成都先导","jys":"sh"},{"dm":"603919","mc":"金徽酒","jys":"sh"},{"dm":"600507","mc":"方大特钢","jys":"sh"},{"dm":"001896","mc":"豫能控股","jys":"sz"},{"dm":"600821","mc":"金开新能","jys":"sh"},{"dm":"600826","mc":"兰生股份","jys":"sh"},{"dm":"603915","mc":"国茂股份","jys":"sh"},{"dm":"003002","mc":"壶化股份","jys":"sz"},{"dm":"600745","mc":"闻泰科技","jys":"sh"},{"dm":"300204","mc":"舒泰神","jys":"sz"},{"dm":"688553","mc":"汇宇制药-W","jys":"sh"},{"dm":"300012","mc":"华测检测","jys":"sz"},{"dm":"002074","mc":"国轩高科","jys":"sz"},{"dm":"301371","mc":"敷尔佳","jys":"sz"},{"dm":"000717","mc":"中南股份","jys":"sz"},{"dm":"300194","mc":"福安药业","jys":"sz"},{"dm":"002384","mc":"东山精密","jys":"sz"},{"dm":"688017","mc":"绿的谐波","jys":"sh"},{"dm":"001301","mc":"尚太科技","jys":"sz"},{"dm":"603129","mc":"春风动力","jys":"sh"},{"dm":"002773","mc":"康弘药业","jys":"sz"},{"dm":"301367","mc":"怡和嘉业","jys":"sz"},{"dm":"688507","mc":"索辰科技","jys":"sh"},{"dm":"002111","mc":"威海广泰","jys":"sz"},{"dm":"601882","mc":"海天精工","jys":"sh"},{"dm":"688326","mc":"经纬恒润-W","jys":"sh"},{"dm":"300439","mc":"美康生物","jys":"sz"},{"dm":"688065","mc":"凯赛生物","jys":"sh"},{"dm":"603838","mc":"四通股份","jys":"sh"},{"dm":"301207","mc":"华兰疫苗","jys":"sz"},{"dm":"688303","mc":"大全能源","jys":"sh"},{"dm":"300726","mc":"宏达电子","jys":"sz"},{"dm":"603232","mc":"格尔软件","jys":"sh"},{"dm":"002481","mc":"双塔食品","jys":"sz"},{"dm":"603828","mc":"柯利达","jys":"sh"},{"dm":"300198","mc":"纳川股份","jys":"sz"},{"dm":"002505","mc":"鹏都农牧","jys":"sz"},{"dm":"688189","mc":"南新制药","jys":"sh"},{"dm":"603187","mc":"海容冷链","jys":"sh"},{"dm":"301012","mc":"扬电科技","jys":"sz"},{"dm":"300406","mc":"九强生物","jys":"sz"},{"dm":"300341","mc":"麦克奥迪","jys":"sz"},{"dm":"600309","mc":"万华化学","jys":"sh"},{"dm":"000590","mc":"启迪药业","jys":"sz"},{"dm":"002460","mc":"赣锋锂业","jys":"sz"},{"dm":"601577","mc":"长沙银行","jys":"sh"},{"dm":"600036","mc":"招商银行","jys":"sh"},{"dm":"603612","mc":"索通发展","jys":"sh"},{"dm":"603096","mc":"新经典","jys":"sh"},{"dm":"688157","mc":"松井股份","jys":"sh"},{"dm":"688053","mc":"思科瑞","jys":"sh"},{"dm":"603676","mc":"卫信康","jys":"sh"},{"dm":"600070","mc":"ST富润","jys":"sh"},{"dm":"002433","mc":"*ST太安","jys":"sz"},{"dm":"603277","mc":"银都股份","jys":"sh"},{"dm":"603190","mc":"亚通精工","jys":"sh"},{"dm":"603737","mc":"三棵树","jys":"sh"},{"dm":"002308","mc":"威创股份","jys":"sz"},{"dm":"002853","mc":"皮阿诺","jys":"sz"},{"dm":"301533","mc":"威马农机","jys":"sz"},{"dm":"003038","mc":"鑫铂股份","jys":"sz"},{"dm":"603733","mc":"仙鹤股份","jys":"sh"},{"dm":"300677","mc":"英科医疗","jys":"sz"},{"dm":"600531","mc":"豫光金铅","jys":"sh"},{"dm":"603555","mc":"ST贵人","jys":"sh"},{"dm":"601766","mc":"中国中车","jys":"sh"},{"dm":"002242","mc":"九阳股份","jys":"sz"},{"dm":"601636","mc":"旗滨集团","jys":"sh"},{"dm":"600935","mc":"华塑股份","jys":"sh"},{"dm":"300881","mc":"盛德鑫泰","jys":"sz"},{"dm":"688607","mc":"康众医疗","jys":"sh"},{"dm":"688085","mc":"三友医疗","jys":"sh"},{"dm":"600882","mc":"妙可蓝多","jys":"sh"},{"dm":"300181","mc":"佐力药业","jys":"sz"},{"dm":"603758","mc":"秦安股份","jys":"sh"},{"dm":"000785","mc":"居然之家","jys":"sz"},{"dm":"600116","mc":"三峡水利","jys":"sh"},{"dm":"300015","mc":"爱尔眼科","jys":"sz"},{"dm":"000692","mc":"*ST惠天","jys":"sz"},{"dm":"300986","mc":"志特新材","jys":"sz"},{"dm":"002508","mc":"老板电器","jys":"sz"},{"dm":"002800","mc":"ST天顺","jys":"sz"},{"dm":"301239","mc":"普瑞眼科","jys":"sz"},{"dm":"600383","mc":"金地集团","jys":"sh"},{"dm":"301555","mc":"惠柏新材","jys":"sz"},{"dm":"000629","mc":"钒钛股份","jys":"sz"},{"dm":"000882","mc":"华联股份","jys":"sz"},{"dm":"605258","mc":"协和电子","jys":"sh"},{"dm":"605337","mc":"李子园","jys":"sh"},{"dm":"603590","mc":"康辰药业","jys":"sh"},{"dm":"300648","mc":"星云股份","jys":"sz"},{"dm":"600519","mc":"贵州茅台","jys":"sh"},{"dm":"001322","mc":"箭牌家居","jys":"sz"},{"dm":"600511","mc":"国药股份","jys":"sh"},{"dm":"000898","mc":"鞍钢股份","jys":"sz"},{"dm":"000501","mc":"武商集团","jys":"sz"},{"dm":"301498","mc":"乖宝宠物","jys":"sz"},{"dm":"301186","mc":"超达装备","jys":"sz"},{"dm":"688217","mc":"睿昂基因","jys":"sh"},{"dm":"600189","mc":"泉阳泉","jys":"sh"},{"dm":"688295","mc":"中复神鹰","jys":"sh"},{"dm":"600843","mc":"上工申贝","jys":"sh"},{"dm":"605189","mc":"富春染织","jys":"sh"},{"dm":"601390","mc":"中国中铁","jys":"sh"},{"dm":"688570","mc":"天玛智控","jys":"sh"},{"dm":"600960","mc":"渤海汽车","jys":"sh"},{"dm":"688472","mc":"阿特斯","jys":"sh"},{"dm":"002120","mc":"韵达股份","jys":"sz"},{"dm":"603688","mc":"石英股份","jys":"sh"},{"dm":"300002","mc":"神州泰岳","jys":"sz"},{"dm":"600115","mc":"中国东航","jys":"sh"},{"dm":"300583","mc":"赛托生物","jys":"sz"},{"dm":"600011","mc":"华能国际","jys":"sh"},{"dm":"000878","mc":"云南铜业","jys":"sz"},{"dm":"002332","mc":"仙琚制药","jys":"sz"},{"dm":"603855","mc":"华荣股份","jys":"sh"},{"dm":"600743","mc":"华远地产","jys":"sh"},{"dm":"600673","mc":"东阳光","jys":"sh"},{"dm":"603071","mc":"物产环能","jys":"sh"},{"dm":"002667","mc":"威领股份","jys":"sz"},{"dm":"600499","mc":"科达制造","jys":"sh"},{"dm":"300251","mc":"光线传媒","jys":"sz"},{"dm":"603369","mc":"今世缘","jys":"sh"},{"dm":"301408","mc":"华人健康","jys":"sz"},{"dm":"600729","mc":"重庆百货","jys":"sh"},{"dm":"002461","mc":"珠江啤酒","jys":"sz"},{"dm":"300358","mc":"楚天科技","jys":"sz"},{"dm":"300988","mc":"津荣天宇","jys":"sz"},{"dm":"603755","mc":"日辰股份","jys":"sh"},{"dm":"603281","mc":"江瀚新材","jys":"sh"},{"dm":"688601","mc":"力芯微","jys":"sh"},{"dm":"603233","mc":"大参林","jys":"sh"},{"dm":"002219","mc":"新里程","jys":"sz"},{"dm":"603007","mc":"ST花王","jys":"sh"},{"dm":"002624","mc":"完美世界","jys":"sz"},{"dm":"300705","mc":"九典制药","jys":"sz"},{"dm":"600809","mc":"山西汾酒","jys":"sh"},{"dm":"600761","mc":"安徽合力","jys":"sh"},{"dm":"002597","mc":"金禾实业","jys":"sz"},{"dm":"601567","mc":"三星医疗","jys":"sh"},{"dm":"600323","mc":"瀚蓝环境","jys":"sh"},{"dm":"002520","mc":"日发精机","jys":"sz"},{"dm":"002812","mc":"恩捷股份","jys":"sz"},{"dm":"600741","mc":"华域汽车","jys":"sh"},{"dm":"002240","mc":"盛新锂能","jys":"sz"},{"dm":"001266","mc":"宏英智能","jys":"sz"},{"dm":"300404","mc":"博济医药","jys":"sz"},{"dm":"301141","mc":"中科磁业","jys":"sz"},{"dm":"300664","mc":"鹏鹞环保","jys":"sz"},{"dm":"600208","mc":"新湖中宝","jys":"sh"},{"dm":"000657","mc":"中钨高新","jys":"sz"},{"dm":"688526","mc":"科前生物","jys":"sh"},{"dm":"605177","mc":"东亚药业","jys":"sh"},{"dm":"603259","mc":"药明康德","jys":"sh"},{"dm":"601186","mc":"中国铁建","jys":"sh"},{"dm":"600977","mc":"中国电影","jys":"sh"},{"dm":"605300","mc":"佳禾食品","jys":"sh"},{"dm":"002324","mc":"普利特","jys":"sz"},{"dm":"688779","mc":"长远锂科","jys":"sh"},{"dm":"603867","mc":"新化股份","jys":"sh"},{"dm":"300633","mc":"开立医疗","jys":"sz"},{"dm":"603180","mc":"金牌厨柜","jys":"sh"},{"dm":"601208","mc":"东材科技","jys":"sh"},{"dm":"000415","mc":"渤海租赁","jys":"sz"},{"dm":"002175","mc":"东方智造","jys":"sz"},{"dm":"300731","mc":"科创新源","jys":"sz"},{"dm":"600776","mc":"东方通信","jys":"sh"},{"dm":"600811","mc":"东方集团","jys":"sh"},{"dm":"605377","mc":"华旺科技","jys":"sh"},{"dm":"688200","mc":"华峰测控","jys":"sh"},{"dm":"603566","mc":"普莱柯","jys":"sh"},{"dm":"300415","mc":"伊之密","jys":"sz"},{"dm":"600640","mc":"国脉文化","jys":"sh"},{"dm":"000796","mc":"*ST凯撒","jys":"sz"},{"dm":"002867","mc":"周大生","jys":"sz"},{"dm":"300593","mc":"新雷能","jys":"sz"},{"dm":"301190","mc":"善水科技","jys":"sz"},{"dm":"300332","mc":"天壕能源","jys":"sz"},{"dm":"000065","mc":"北方国际","jys":"sz"},{"dm":"600782","mc":"新钢股份","jys":"sh"},{"dm":"002119","mc":"康强电子","jys":"sz"},{"dm":"603079","mc":"圣达生物","jys":"sh"},{"dm":"688005","mc":"容百科技","jys":"sh"},{"dm":"002009","mc":"天奇股份","jys":"sz"},{"dm":"002608","mc":"江苏国信","jys":"sz"},{"dm":"002482","mc":"*ST广田","jys":"sz"},{"dm":"300436","mc":"广生堂","jys":"sz"},{"dm":"688121","mc":"卓然股份","jys":"sh"},{"dm":"600219","mc":"南山铝业","jys":"sh"},{"dm":"600301","mc":"华锡有色","jys":"sh"},{"dm":"603806","mc":"福斯特","jys":"sh"},{"dm":"603239","mc":"浙江仙通","jys":"sh"},{"dm":"603711","mc":"香飘飘","jys":"sh"},{"dm":"300363","mc":"博腾股份","jys":"sz"},{"dm":"601975","mc":"招商南油","jys":"sh"},{"dm":"002247","mc":"聚力文化","jys":"sz"},{"dm":"002078","mc":"太阳纸业","jys":"sz"},{"dm":"002379","mc":"宏创控股","jys":"sz"},{"dm":"301093","mc":"华兰股份","jys":"sz"},{"dm":"003012","mc":"东鹏控股","jys":"sz"},{"dm":"002237","mc":"恒邦股份","jys":"sz"},{"dm":"603616","mc":"韩建河山","jys":"sh"},{"dm":"000813","mc":"德展健康","jys":"sz"},{"dm":"002858","mc":"力盛体育","jys":"sz"},{"dm":"002233","mc":"塔牌集团","jys":"sz"},{"dm":"600654","mc":"ST中安","jys":"sh"},{"dm":"002110","mc":"三钢闽光","jys":"sz"},{"dm":"688536","mc":"思瑞浦","jys":"sh"},{"dm":"601668","mc":"中国建筑","jys":"sh"},{"dm":"688566","mc":"吉贝尔","jys":"sh"},{"dm":"600572","mc":"康恩贝","jys":"sh"},{"dm":"300144","mc":"宋城演艺","jys":"sz"},{"dm":"600176","mc":"中国巨石","jys":"sh"},{"dm":"601005","mc":"重庆钢铁","jys":"sh"},{"dm":"300143","mc":"盈康生命","jys":"sz"},{"dm":"688108","mc":"赛诺医疗","jys":"sh"},{"dm":"603768","mc":"常青股份","jys":"sh"},{"dm":"002831","mc":"裕同科技","jys":"sz"},{"dm":"603268","mc":"松发股份","jys":"sh"},{"dm":"002154","mc":"报 喜 鸟","jys":"sz"},{"dm":"603559","mc":"ST通脉","jys":"sh"},{"dm":"002475","mc":"立讯精密","jys":"sz"},{"dm":"688513","mc":"苑东生物","jys":"sh"},{"dm":"688137","mc":"近岸蛋白","jys":"sh"},{"dm":"603898","mc":"好莱客","jys":"sh"},{"dm":"603801","mc":"志邦家居","jys":"sh"},{"dm":"600486","mc":"扬农化工","jys":"sh"},{"dm":"000807","mc":"云铝股份","jys":"sz"},{"dm":"002906","mc":"华阳集团","jys":"sz"},{"dm":"002284","mc":"亚太股份","jys":"sz"},{"dm":"600221","mc":"海航控股","jys":"sh"},{"dm":"300370","mc":"安控科技","jys":"sz"},{"dm":"300175","mc":"朗源股份","jys":"sz"},{"dm":"300275","mc":"梅安森","jys":"sz"},{"dm":"688670","mc":"金迪克","jys":"sh"},{"dm":"603027","mc":"千禾味业","jys":"sh"},{"dm":"301349","mc":"信德新材","jys":"sz"},{"dm":"000516","mc":"国际医学","jys":"sz"},{"dm":"300855","mc":"图南股份","jys":"sz"},{"dm":"688180","mc":"君实生物-U","jys":"sh"},{"dm":"002928","mc":"华夏航空","jys":"sz"},{"dm":"603716","mc":"塞力医疗","jys":"sh"},{"dm":"300873","mc":"海晨股份","jys":"sz"},{"dm":"002756","mc":"永兴材料","jys":"sz"},{"dm":"605566","mc":"福莱蒽特","jys":"sh"},{"dm":"603168","mc":"莎普爱思","jys":"sh"},{"dm":"301091","mc":"深城交","jys":"sz"},{"dm":"300398","mc":"飞凯材料","jys":"sz"},{"dm":"301269","mc":"华大九天","jys":"sz"},{"dm":"603939","mc":"益丰药房","jys":"sh"},{"dm":"300434","mc":"金石亚药","jys":"sz"},{"dm":"605266","mc":"健之佳","jys":"sh"},{"dm":"688235","mc":"百济神州-U","jys":"sh"},{"dm":"603208","mc":"江山欧派","jys":"sh"},{"dm":"300299","mc":"富春股份","jys":"sz"},{"dm":"600305","mc":"恒顺醋业","jys":"sh"},{"dm":"603987","mc":"康德莱","jys":"sh"},{"dm":"603767","mc":"中马传动","jys":"sh"},{"dm":"002273","mc":"水晶光电","jys":"sz"},{"dm":"002320","mc":"海峡股份","jys":"sz"},{"dm":"600749","mc":"西藏旅游","jys":"sh"},{"dm":"000541","mc":"佛山照明","jys":"sz"},{"dm":"300488","mc":"恒锋工具","jys":"sz"},{"dm":"300258","mc":"精锻科技","jys":"sz"},{"dm":"601677","mc":"明泰铝业","jys":"sh"},{"dm":"603219","mc":"富佳股份","jys":"sh"},{"dm":"600763","mc":"通策医疗","jys":"sh"},{"dm":"300737","mc":"科顺股份","jys":"sz"},{"dm":"688276","mc":"百克生物","jys":"sh"},{"dm":"301358","mc":"湖南裕能","jys":"sz"},{"dm":"300231","mc":"银信科技","jys":"sz"},{"dm":"300441","mc":"鲍斯股份","jys":"sz"},{"dm":"600482","mc":"中国动力","jys":"sh"},{"dm":"688291","mc":"金橙子","jys":"sh"},{"dm":"300767","mc":"震安科技","jys":"sz"},{"dm":"603429","mc":"集友股份","jys":"sh"},{"dm":"000695","mc":"滨海能源","jys":"sz"},{"dm":"601958","mc":"金钼股份","jys":"sh"},{"dm":"601089","mc":"福元医药","jys":"sh"},{"dm":"601665","mc":"齐鲁银行","jys":"sh"},{"dm":"600647","mc":"*ST同达","jys":"sh"},{"dm":"688562","mc":"航天软件","jys":"sh"},{"dm":"300953","mc":"震裕科技","jys":"sz"},{"dm":"000672","mc":"上峰水泥","jys":"sz"},{"dm":"300545","mc":"联得装备","jys":"sz"},{"dm":"301210","mc":"金杨股份","jys":"sz"},{"dm":"600988","mc":"赤峰黄金","jys":"sh"},{"dm":"300151","mc":"昌红科技","jys":"sz"},{"dm":"000739","mc":"普洛药业","jys":"sz"},{"dm":"002977","mc":"天箭科技","jys":"sz"},{"dm":"688358","mc":"祥生医疗","jys":"sh"},{"dm":"688221","mc":"前沿生物-U","jys":"sh"},{"dm":"600179","mc":"安通控股","jys":"sh"},{"dm":"000996","mc":"*ST中期","jys":"sz"},{"dm":"002426","mc":"胜利精密","jys":"sz"},{"dm":"300985","mc":"致远新能","jys":"sz"},{"dm":"688558","mc":"国盛智科","jys":"sh"},{"dm":"688275","mc":"万润新能","jys":"sh"},{"dm":"300418","mc":"昆仑万维","jys":"sz"},{"dm":"688510","mc":"航亚科技","jys":"sh"},{"dm":"300818","mc":"耐普矿机","jys":"sz"},{"dm":"600215","mc":"派斯林","jys":"sh"},{"dm":"688658","mc":"悦康药业","jys":"sh"},{"dm":"002341","mc":"新纶新材","jys":"sz"},{"dm":"688212","mc":"澳华内镜","jys":"sh"},{"dm":"301235","mc":"华康医疗","jys":"sz"},{"dm":"000893","mc":"亚钾国际","jys":"sz"},{"dm":"600719","mc":"大连热电","jys":"sh"},{"dm":"300661","mc":"圣邦股份","jys":"sz"},{"dm":"002375","mc":"亚厦股份","jys":"sz"},{"dm":"000888","mc":"峨眉山A","jys":"sz"},{"dm":"300318","mc":"博晖创新","jys":"sz"},{"dm":"000155","mc":"川能动力","jys":"sz"},{"dm":"600476","mc":"湘邮科技","jys":"sh"},{"dm":"688631","mc":"莱斯信息","jys":"sh"},{"dm":"688408","mc":"中信博","jys":"sh"},{"dm":"002430","mc":"杭氧股份","jys":"sz"},{"dm":"300123","mc":"亚光科技","jys":"sz"},{"dm":"601163","mc":"三角轮胎","jys":"sh"},{"dm":"605369","mc":"拱东医疗","jys":"sh"},{"dm":"300832","mc":"新产业","jys":"sz"},{"dm":"003018","mc":"金富科技","jys":"sz"},{"dm":"300375","mc":"鹏翎股份","jys":"sz"},{"dm":"300685","mc":"艾德生物","jys":"sz"},{"dm":"603901","mc":"永创智能","jys":"sh"},{"dm":"002370","mc":"亚太药业","jys":"sz"},{"dm":"605368","mc":"蓝天燃气","jys":"sh"},{"dm":"601318","mc":"中国平安","jys":"sh"},{"dm":"002470","mc":"金正大","jys":"sz"},{"dm":"600233","mc":"圆通速递","jys":"sh"},{"dm":"603196","mc":"日播时尚","jys":"sh"},{"dm":"603136","mc":"天目湖","jys":"sh"},{"dm":"002699","mc":"*ST美盛","jys":"sz"},{"dm":"688050","mc":"爱博医疗","jys":"sh"},{"dm":"688657","mc":"浩辰软件","jys":"sh"},{"dm":"002664","mc":"信质集团","jys":"sz"},{"dm":"603107","mc":"C汽配","jys":"sh"},{"dm":"601872","mc":"招商轮船","jys":"sh"},{"dm":"688556","mc":"高测股份","jys":"sh"},{"dm":"603538","mc":"美诺华","jys":"sh"},{"dm":"688238","mc":"和元生物","jys":"sh"},{"dm":"300014","mc":"亿纬锂能","jys":"sz"},{"dm":"603009","mc":"北特科技","jys":"sh"},{"dm":"600026","mc":"中远海能","jys":"sh"},{"dm":"603416","mc":"信捷电气","jys":"sh"},{"dm":"688484","mc":"南芯科技","jys":"sh"},{"dm":"603055","mc":"台华新材","jys":"sh"},{"dm":"301306","mc":"西测测试","jys":"sz"},{"dm":"002099","mc":"海翔药业","jys":"sz"},{"dm":"301488","mc":"豪恩汽电","jys":"sz"},{"dm":"605108","mc":"同庆楼","jys":"sh"},{"dm":"000697","mc":"*ST炼石","jys":"sz"},{"dm":"300645","mc":"正元智慧","jys":"sz"},{"dm":"002976","mc":"瑞玛精密","jys":"sz"},{"dm":"000049","mc":"德赛电池","jys":"sz"},{"dm":"002566","mc":"益盛药业","jys":"sz"},{"dm":"002337","mc":"赛象科技","jys":"sz"},{"dm":"002068","mc":"黑猫股份","jys":"sz"},{"dm":"300571","mc":"平治信息","jys":"sz"},{"dm":"600023","mc":"浙能电力","jys":"sh"},{"dm":"688345","mc":"博力威","jys":"sh"},{"dm":"600569","mc":"安阳钢铁","jys":"sh"},{"dm":"000676","mc":"智度股份","jys":"sz"},{"dm":"300470","mc":"中密控股","jys":"sz"},{"dm":"600362","mc":"江西铜业","jys":"sh"},{"dm":"688131","mc":"皓元医药","jys":"sh"},{"dm":"603108","mc":"润达医疗","jys":"sh"},{"dm":"688719","mc":"爱科赛博","jys":"sh"},{"dm":"300207","mc":"欣旺达","jys":"sz"},{"dm":"000680","mc":"山推股份","jys":"sz"},{"dm":"300408","mc":"三环集团","jys":"sz"},{"dm":"002788","mc":"鹭燕医药","jys":"sz"},{"dm":"601702","mc":"华峰铝业","jys":"sh"},{"dm":"002180","mc":"纳思达","jys":"sz"},{"dm":"603222","mc":"济民医疗","jys":"sh"},{"dm":"300233","mc":"金城医药","jys":"sz"},{"dm":"688088","mc":"虹软科技","jys":"sh"},{"dm":"300395","mc":"菲利华","jys":"sz"},{"dm":"601333","mc":"广深铁路","jys":"sh"},{"dm":"600587","mc":"新华医疗","jys":"sh"},{"dm":"300979","mc":"华利集团","jys":"sz"},{"dm":"600754","mc":"锦江酒店","jys":"sh"},{"dm":"688152","mc":"麒麟信安","jys":"sh"},{"dm":"002532","mc":"天山铝业","jys":"sz"},{"dm":"605277","mc":"新亚电子","jys":"sh"},{"dm":"001319","mc":"铭科精技","jys":"sz"},{"dm":"688058","mc":"宝兰德","jys":"sh"},{"dm":"689009","mc":"九号公司-WD","jys":"sh"},{"dm":"300857","mc":"协创数据","jys":"sz"},{"dm":"300391","mc":"长药控股","jys":"sz"},{"dm":"603529","mc":"爱玛科技","jys":"sh"},{"dm":"002713","mc":"东易日盛","jys":"sz"},{"dm":"688302","mc":"海创药业-U","jys":"sh"},{"dm":"603048","mc":"浙江黎明","jys":"sh"},{"dm":"002708","mc":"光洋股份","jys":"sz"},{"dm":"002541","mc":"鸿路钢构","jys":"sz"},{"dm":"600575","mc":"淮河能源","jys":"sh"},{"dm":"002870","mc":"香山股份","jys":"sz"},{"dm":"002738","mc":"中矿资源","jys":"sz"},{"dm":"300034","mc":"钢研高纳","jys":"sz"},{"dm":"688110","mc":"东芯股份","jys":"sh"},{"dm":"688150","mc":"莱特光电","jys":"sh"},{"dm":"000711","mc":"*ST京蓝","jys":"sz"},{"dm":"688261","mc":"东微半导","jys":"sh"},{"dm":"600851","mc":"海欣股份","jys":"sh"},{"dm":"301368","mc":"丰立智能","jys":"sz"},{"dm":"600771","mc":"广誉远","jys":"sh"},{"dm":"002793","mc":"罗欣药业","jys":"sz"},{"dm":"301285","mc":"鸿日达","jys":"sz"},{"dm":"688581","mc":"安杰思","jys":"sh"},{"dm":"688308","mc":"欧科亿","jys":"sh"},{"dm":"002123","mc":"梦网科技","jys":"sz"},{"dm":"002182","mc":"宝武镁业","jys":"sz"},{"dm":"688433","mc":"华曙高科","jys":"sh"},{"dm":"300083","mc":"创世纪","jys":"sz"},{"dm":"688029","mc":"南微医学","jys":"sh"},{"dm":"000488","mc":"晨鸣纸业","jys":"sz"},{"dm":"601319","mc":"中国人保","jys":"sh"},{"dm":"600333","mc":"长春燃气","jys":"sh"},{"dm":"603983","mc":"丸美股份","jys":"sh"},{"dm":"603617","mc":"君禾股份","jys":"sh"},{"dm":"001914","mc":"招商积余","jys":"sz"},{"dm":"002634","mc":"棒杰股份","jys":"sz"},{"dm":"002596","mc":"海南瑞泽","jys":"sz"},{"dm":"002007","mc":"华兰生物","jys":"sz"},{"dm":"603015","mc":"弘讯科技","jys":"sh"},{"dm":"002648","mc":"卫星化学","jys":"sz"},{"dm":"300910","mc":"瑞丰新材","jys":"sz"},{"dm":"603699","mc":"纽威股份","jys":"sh"},{"dm":"600657","mc":"信达地产","jys":"sh"},{"dm":"688173","mc":"希荻微","jys":"sh"},{"dm":"688392","mc":"骄成超声","jys":"sh"},{"dm":"688676","mc":"金盘科技","jys":"sh"},{"dm":"603098","mc":"森特股份","jys":"sh"},{"dm":"000708","mc":"中信特钢","jys":"sz"},{"dm":"688059","mc":"华锐精密","jys":"sh"},{"dm":"601336","mc":"新华保险","jys":"sh"},{"dm":"300430","mc":"诚益通","jys":"sz"},{"dm":"600258","mc":"首旅酒店","jys":"sh"},{"dm":"688153","mc":"唯捷创芯","jys":"sh"},{"dm":"301042","mc":"安联锐视","jys":"sz"},{"dm":"000528","mc":"柳 工","jys":"sz"},{"dm":"688325","mc":"赛微微电","jys":"sh"},{"dm":"603986","mc":"兆易创新","jys":"sh"},{"dm":"002277","mc":"友阿股份","jys":"sz"},{"dm":"301550","mc":"斯菱股份","jys":"sz"},{"dm":"603456","mc":"九洲药业","jys":"sh"},{"dm":"600529","mc":"山东药玻","jys":"sh"},{"dm":"000963","mc":"华东医药","jys":"sz"},{"dm":"600246","mc":"万通发展","jys":"sh"},{"dm":"002709","mc":"天赐材料","jys":"sz"},{"dm":"688052","mc":"纳芯微","jys":"sh"},{"dm":"300535","mc":"达威股份","jys":"sz"},{"dm":"300291","mc":"百纳千成","jys":"sz"},{"dm":"300266","mc":"兴源环境","jys":"sz"},{"dm":"603766","mc":"隆鑫通用","jys":"sh"},{"dm":"600563","mc":"法拉电子","jys":"sh"},{"dm":"301397","mc":"溯联股份","jys":"sz"},{"dm":"002138","mc":"顺络电子","jys":"sz"},{"dm":"000060","mc":"中金岭南","jys":"sz"},{"dm":"300196","mc":"长海股份","jys":"sz"},{"dm":"603195","mc":"公牛集团","jys":"sh"},{"dm":"603899","mc":"晨光股份","jys":"sh"},{"dm":"301230","mc":"泓博医药","jys":"sz"},{"dm":"000975","mc":"银泰黄金","jys":"sz"},{"dm":"002888","mc":"惠威科技","jys":"sz"},{"dm":"688156","mc":"路德环境","jys":"sh"},{"dm":"000543","mc":"皖能电力","jys":"sz"},{"dm":"688301","mc":"奕瑞科技","jys":"sh"},{"dm":"688596","mc":"正帆科技","jys":"sh"},{"dm":"002826","mc":"易明医药","jys":"sz"},{"dm":"000793","mc":"华闻集团","jys":"sz"},{"dm":"688679","mc":"通源环境","jys":"sh"},{"dm":"688371","mc":"菲沃泰","jys":"sh"},{"dm":"600078","mc":"ST澄星","jys":"sh"},{"dm":"001367","mc":"海森药业","jys":"sz"},{"dm":"002727","mc":"一心堂","jys":"sz"},{"dm":"601966","mc":"玲珑轮胎","jys":"sh"},{"dm":"601628","mc":"中国人寿","jys":"sh"},{"dm":"002903","mc":"宇环数控","jys":"sz"},{"dm":"688383","mc":"新益昌","jys":"sh"},{"dm":"603028","mc":"赛福天","jys":"sh"},{"dm":"000892","mc":"欢瑞世纪","jys":"sz"},{"dm":"600186","mc":"莲花健康","jys":"sh"},{"dm":"688443","mc":"智翔金泰-U","jys":"sh"},{"dm":"603383","mc":"顶点软件","jys":"sh"},{"dm":"600801","mc":"华新水泥","jys":"sh"},{"dm":"603163","mc":"圣晖集成","jys":"sh"},{"dm":"688332","mc":"中科蓝讯","jys":"sh"},{"dm":"300580","mc":"贝斯特","jys":"sz"},{"dm":"300102","mc":"乾照光电","jys":"sz"},{"dm":"688799","mc":"华纳药厂","jys":"sh"},{"dm":"688019","mc":"安集科技","jys":"sh"},{"dm":"001308","mc":"康冠科技","jys":"sz"},{"dm":"300280","mc":"紫天科技","jys":"sz"},{"dm":"300269","mc":"联建光电","jys":"sz"},{"dm":"300093","mc":"金刚光伏","jys":"sz"},{"dm":"000503","mc":"国新健康","jys":"sz"},{"dm":"688223","mc":"晶科能源","jys":"sh"},{"dm":"600285","mc":"羚锐制药","jys":"sh"},{"dm":"688595","mc":"芯海科技","jys":"sh"},{"dm":"688377","mc":"迪威尔","jys":"sh"},{"dm":"300122","mc":"智飞生物","jys":"sz"},{"dm":"300750","mc":"宁德时代","jys":"sz"},{"dm":"300606","mc":"金太阳","jys":"sz"},{"dm":"300709","mc":"精研科技","jys":"sz"},{"dm":"002151","mc":"北斗星通","jys":"sz"},{"dm":"300926","mc":"博俊科技","jys":"sz"},{"dm":"002107","mc":"沃华医药","jys":"sz"},{"dm":"688239","mc":"航宇科技","jys":"sh"},{"dm":"688356","mc":"键凯科技","jys":"sh"},{"dm":"600398","mc":"海澜之家","jys":"sh"},{"dm":"688177","mc":"百奥泰","jys":"sh"},{"dm":"002818","mc":"富森美","jys":"sz"},{"dm":"688333","mc":"铂力特","jys":"sh"},{"dm":"601799","mc":"星宇股份","jys":"sh"},{"dm":"688220","mc":"翱捷科技-U","jys":"sh"},{"dm":"600066","mc":"宇通客车","jys":"sh"},{"dm":"603786","mc":"科博达","jys":"sh"},{"dm":"301236","mc":"软通动力","jys":"sz"},{"dm":"002203","mc":"海亮股份","jys":"sz"},{"dm":"600513","mc":"联环药业","jys":"sh"},{"dm":"688318","mc":"财富趋势","jys":"sh"},{"dm":"600266","mc":"城建发展","jys":"sh"},{"dm":"603730","mc":"岱美股份","jys":"sh"},{"dm":"300539","mc":"横河精密","jys":"sz"},{"dm":"301319","mc":"唯特偶","jys":"sz"},{"dm":"002513","mc":"蓝丰生化","jys":"sz"},{"dm":"301289","mc":"国缆检测","jys":"sz"},{"dm":"002262","mc":"恩华药业","jys":"sz"},{"dm":"000603","mc":"盛达资源","jys":"sz"},{"dm":"688531","mc":"日联科技","jys":"sh"},{"dm":"603008","mc":"喜临门","jys":"sh"},{"dm":"603666","mc":"亿嘉和","jys":"sh"},{"dm":"300584","mc":"海辰药业","jys":"sz"},{"dm":"600119","mc":"长江投资","jys":"sh"},{"dm":"603885","mc":"吉祥航空","jys":"sh"},{"dm":"300833","mc":"浩洋股份","jys":"sz"},{"dm":"600649","mc":"城投控股","jys":"sh"},{"dm":"600155","mc":"华创云信","jys":"sh"},{"dm":"300683","mc":"海特生物","jys":"sz"},{"dm":"688188","mc":"柏楚电子","jys":"sh"},{"dm":"002963","mc":"豪尔赛","jys":"sz"},{"dm":"300933","mc":"中辰股份","jys":"sz"},{"dm":"601127","mc":"赛力斯","jys":"sh"},{"dm":"300169","mc":"天晟新材","jys":"sz"},{"dm":"688351","mc":"微电生理-U","jys":"sh"},{"dm":"300735","mc":"光弘科技","jys":"sz"},{"dm":"688236","mc":"春立医疗","jys":"sh"},{"dm":"688617","mc":"惠泰医疗","jys":"sh"},{"dm":"601601","mc":"中国太保","jys":"sh"},{"dm":"600593","mc":"大连圣亚","jys":"sh"},{"dm":"002572","mc":"索菲亚","jys":"sz"},{"dm":"688210","mc":"统联精密","jys":"sh"},{"dm":"688260","mc":"昀冢科技","jys":"sh"},{"dm":"688036","mc":"传音控股","jys":"sh"},{"dm":"300313","mc":"*ST天山","jys":"sz"},{"dm":"603929","mc":"亚翔集成","jys":"sh"},{"dm":"300765","mc":"新诺威","jys":"sz"},{"dm":"300049","mc":"福瑞股份","jys":"sz"},{"dm":"688028","mc":"沃尔德","jys":"sh"},{"dm":"001376","mc":"C百通","jys":"sz"}] \ No newline at end of file diff --git a/coagent/tools/weather.py b/coagent/tools/weather.py deleted file mode 100644 index bfb5b02..0000000 --- a/coagent/tools/weather.py +++ /dev/null @@ -1,109 +0,0 @@ - -import json -import os -import re -from pydantic import BaseModel, Field -from typing import List, Dict -import requests -from loguru import logger - -from .base_tool import BaseToolModel - - - -class WeatherInfo(BaseToolModel): - """ - Tips: - default control Required, e.g. key1 is not Required/key2 is Required - """ - - name: str = "WeatherInfo" - description: str = "According to the user's input adcode, it can query the current/future weather conditions of the target area." - - class ToolInputArgs(BaseModel): - """Input for Weather.""" - - # key: str = Field(..., description="用户在高德地图官网申请web服务API类型KEY") - city: str = Field(..., description="城市编码,输入城市的adcode,adcode信息可参考城市编码表") - extensions: str = Field(default=None, enum=["base", "all"], description="气象类型,输入城市的adcode,adcode信息可参考城市编码表") - - class ToolOutputArgs(BaseModel): - """Output for Weather.""" - - lives: str = Field(default=None, description="实况天气数据") - - # @classmethod - # def run(cls, tool_input_args: ToolInputArgs) -> ToolOutputArgs: - # """excute your tool!""" - # url = "https://restapi.amap.com/v3/weather/weatherInfo" - # try: - # json_data = tool_input_args.dict() - # json_data["key"] = "4ceb2ef6257a627b72e3be6beab5b059" - # res = requests.get(url, json_data) - # return res.json() - # except Exception as e: - # return e - - @staticmethod - def run(city, extensions) -> ToolOutputArgs: - """excute your tool!""" - url = "https://restapi.amap.com/v3/weather/weatherInfo" - try: - json_data = {} - json_data["city"] = city - json_data["key"] = "4ceb2ef6257a627b72e3be6beab5b059" - json_data["extensions"] = extensions - logger.debug(f"json_data: {json_data}") - res = requests.get(url, params=json_data) - return res.json() - except Exception as e: - return e - - -class DistrictInfo(BaseToolModel): - """ - Tips: - default control Required, e.g. key1 is not Required/key2 is Required - """ - - name: str = "DistrictInfo" - description: str = "用户希望通过得到行政区域信息,进行开发工作。" - - class ToolInputArgs(BaseModel): - """Input for district.""" - keywords: str = Field(default=None, description="规则:只支持单个关键词语搜索关键词支持:行政区名称、citycode、adcode例如,在subdistrict=2,搜索省份(例如山东),能够显示市(例如济南),区(例如历下区)") - subdistrict: str = Field(default=None, enums=[1,2,3], description="""规则:设置显示下级行政区级数(行政区级别包括:国家、省/直辖市、市、区/县、乡镇/街道多级数据) - -可选值:0、1、2、3等数字,并以此类推 - -0:不返回下级行政区; - -1:返回下一级行政区; - -2:返回下两级行政区; - -3:返回下三级行政区;""") - page: int = Field(default=1, examples=["page=2", "page=3"], description="最外层的districts最多会返回20个数据,若超过限制,请用page请求下一页数据。") - extensions: str = Field(default=None, enum=["base", "all"], description="气象类型,输入城市的adcode,adcode信息可参考城市编码表") - - class ToolOutputArgs(BaseModel): - """Output for district.""" - - districts: str = Field(default=None, description="行政区列表") - - @staticmethod - def run(keywords=None, subdistrict=None, page=1, extensions=None) -> ToolOutputArgs: - """excute your tool!""" - url = "https://restapi.amap.com/v3/config/district" - try: - json_data = {} - json_data["keywords"] = keywords - json_data["key"] = "4ceb2ef6257a627b72e3be6beab5b059" - json_data["subdistrict"] = subdistrict - json_data["page"] = page - json_data["extensions"] = extensions - logger.debug(f"json_data: {json_data}") - res = requests.get(url, params=json_data) - return res.json() - except Exception as e: - return e diff --git a/coagent/tools/world_time.py b/coagent/tools/world_time.py deleted file mode 100644 index 642a280..0000000 --- a/coagent/tools/world_time.py +++ /dev/null @@ -1,255 +0,0 @@ -import json -import os -import re -import requests -from pydantic import BaseModel, Field -from typing import List - -from .base_tool import BaseToolModel - - -class WorldTimeGetTimezoneByArea(BaseToolModel): - """ - World Time API - Tips: - default control Required, e.g. key1 is not Required/key2 is Required - """ - - name = "WorldTime.getTimezoneByArea" - description = "a listing of all timezones available for that area." - - class ToolInputArgs(BaseModel): - """Input for WorldTimeGetTimezoneByArea.""" - area: str = Field(..., description="area") - - class ToolOutputArgs(BaseModel): - """Output for WorldTimeGetTimezoneByArea.""" - DateTimeJsonResponse: str = Field(..., description="a list of available timezones") - - @classmethod - def run(area: str) -> ToolOutputArgs: - """excute your tool!""" - url = "http://worldtimeapi.org/api/timezone" - try: - res = requests.get(url, json={"area": area}) - return res.text - except Exception as e: - return e - - -def worldtime_run(area): - url = "http://worldtimeapi.org/api/timezone" - res = requests.get(url, json={"area": area}) - return res.text - -# class WorldTime(BaseTool): -# api_spec: str = ''' -# description: >- -# A simple API to get the current time based on -# a request with a timezone. - -# servers: -# - url: http://worldtimeapi.org/api/ - -# paths: -# /timezone: -# get: -# description: a listing of all timezones. -# operationId: getTimezone -# responses: -# default: -# $ref: "#/components/responses/SuccessfulListJsonResponse" - -# /timezone/{area}: -# get: -# description: a listing of all timezones available for that area. -# operationId: getTimezoneByArea -# parameters: -# - name: area -# in: path -# required: true -# schema: -# type: string -# responses: -# '200': -# $ref: "#/components/responses/SuccessfulListJsonResponse" -# default: -# $ref: "#/components/responses/ErrorJsonResponse" - -# /timezone/{area}/{location}: -# get: -# description: request the current time for a timezone. -# operationId: getTimeByTimezone -# parameters: -# - name: area -# in: path -# required: true -# schema: -# type: string -# - name: location -# in: path -# required: true -# schema: -# type: string -# responses: -# '200': -# $ref: "#/components/responses/SuccessfulDateTimeJsonResponse" -# default: -# $ref: "#/components/responses/ErrorJsonResponse" - -# /ip: -# get: -# description: >- -# request the current time based on the ip of the request. -# note: this is a "best guess" obtained from open-source data. -# operationId: getTimeByIP -# responses: -# '200': -# $ref: "#/components/responses/SuccessfulDateTimeJsonResponse" -# default: -# $ref: "#/components/responses/ErrorJsonResponse" - -# components: -# responses: -# SuccessfulListJsonResponse: -# description: >- -# the list of available timezones in JSON format -# content: -# application/json: -# schema: -# $ref: "#/components/schemas/ListJsonResponse" - -# SuccessfulDateTimeJsonResponse: -# description: >- -# the current time for the timezone requested in JSON format -# content: -# application/json: -# schema: -# $ref: "#/components/schemas/DateTimeJsonResponse" - -# ErrorJsonResponse: -# description: >- -# an error response in JSON format -# content: -# application/json: -# schema: -# $ref: "#/components/schemas/ErrorJsonResponse" - -# schemas: -# ListJsonResponse: -# type: array -# description: >- -# a list of available timezones -# items: -# type: string - -# DateTimeJsonResponse: -# required: -# - abbreviation -# - client_ip -# - datetime -# - day_of_week -# - day_of_year -# - dst -# - dst_offset -# - timezone -# - unixtime -# - utc_datetime -# - utc_offset -# - week_number -# properties: -# abbreviation: -# type: string -# description: >- -# the abbreviated name of the timezone -# client_ip: -# type: string -# description: >- -# the IP of the client making the request -# datetime: -# type: string -# description: >- -# an ISO8601-valid string representing -# the current, local date/time -# day_of_week: -# type: integer -# description: >- -# current day number of the week, where sunday is 0 -# day_of_year: -# type: integer -# description: >- -# ordinal date of the current year -# dst: -# type: boolean -# description: >- -# flag indicating whether the local -# time is in daylight savings -# dst_from: -# type: string -# description: >- -# an ISO8601-valid string representing -# the datetime when daylight savings -# started for this timezone -# dst_offset: -# type: integer -# description: >- -# the difference in seconds between the current local -# time and daylight saving time for the location -# dst_until: -# type: string -# description: >- -# an ISO8601-valid string representing -# the datetime when daylight savings -# will end for this timezone -# raw_offset: -# type: integer -# description: >- -# the difference in seconds between the current local time -# and the time in UTC, excluding any daylight saving difference -# (see dst_offset) -# timezone: -# type: string -# description: >- -# timezone in `Area/Location` or -# `Area/Location/Region` format -# unixtime: -# type: integer -# description: >- -# number of seconds since the Epoch -# utc_datetime: -# type: string -# description: >- -# an ISO8601-valid string representing -# the current date/time in UTC -# utc_offset: -# type: string -# description: >- -# an ISO8601-valid string representing -# the offset from UTC -# week_number: -# type: integer -# description: >- -# the current week number - -# ErrorJsonResponse: -# required: -# - error -# properties: -# error: -# type: string -# description: >- -# details about the error encountered -# ''' - -# def exec_tool(self, message: UserMessage) -> UserMessage: -# match = re.search(r'{[\s\S]*}', message.content) -# if match: -# params = json.loads(match.group()) -# url = params["url"] -# if "params" in params: -# url = url.format(**params["params"]) -# res = requests.get(url) -# response_msg = UserMessage(content=f"API response: {res.text}") -# else: -# raise "ERROR" -# return response_msg \ No newline at end of file diff --git a/coagent/utils/__init__.py b/coagent/utils/__init__.py deleted file mode 100644 index c00d663..0000000 --- a/coagent/utils/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .server_utils import BaseResponse, ListResponse -from .common_utils import func_timer -from .postprocess import replace_lt_gt - -__all__ = [ - "BaseResponse", "ListResponse", "func_timer", 'replace_lt_gt' -] \ No newline at end of file diff --git a/coagent/utils/code2doc_util.py b/coagent/utils/code2doc_util.py deleted file mode 100644 index 7e67923..0000000 --- a/coagent/utils/code2doc_util.py +++ /dev/null @@ -1,91 +0,0 @@ -import json - - -def class_info_decode(data): - '''解析class的相关信息''' - params_dict = {} - - for i in data: - _params_dict = {} - for ii in i: - for k, v in ii.items(): - if k=="origin_query": continue - - if k == "Code Path": - _params_dict["code_path"] = v.split("#")[0] - _params_dict["function_name"] = ".".join(v.split("#")[1:]) - - if k == "Class Description": - _params_dict["ClassDescription"] = v - - if k == "Class Base": - _params_dict["ClassBase"] = v - - if k=="Init Parameters": - _params_dict["Parameters"] = v - - - code_path = _params_dict["code_path"] - params_dict.setdefault(code_path, []).append(_params_dict) - - return params_dict - -def method_info_decode(data): - params_dict = {} - - for i in data: - _params_dict = {} - for ii in i: - for k, v in ii.items(): - if k=="origin_query": continue - - if k == "Code Path": - _params_dict["code_path"] = v.split("#")[0] - _params_dict["function_name"] = ".".join(v.split("#")[1:]) - - if k == "Return Value Description": - _params_dict["Returns"] = v - - if k == "Return Type": - _params_dict["ReturnType"] = v - - if k=="Parameters": - _params_dict["Parameters"] = v - - - code_path = _params_dict["code_path"] - params_dict.setdefault(code_path, []).append(_params_dict) - - return params_dict - -def encode2md(data, md_format): - md_dict = {} - for code_path, params_list in data.items(): - for params in params_list: - params["Parameters_text"] = "\n".join([f"{param['param']}({param['param_type']})-{param['param_description']}" - for param in params["Parameters"]]) - # params.delete("Parameters") - text=md_format.format(**params) - md_dict.setdefault(code_path, []).append(text) - return md_dict - - -method_text_md = ''' -> {function_name} - -| Column Name | Content | -|-----------------|-----------------| -| Parameters | {Parameters_text} | -| Returns | {Returns} | -| Return type | {ReturnType} | -''' - -class_text_md = ''' -> {code_path} - -Bases: {ClassBase} - -{ClassDescription} - -{Parameters_text} -''' \ No newline at end of file diff --git a/coagent/utils/common_utils.py b/coagent/utils/common_utils.py deleted file mode 100644 index ed5c459..0000000 --- a/coagent/utils/common_utils.py +++ /dev/null @@ -1,114 +0,0 @@ -import textwrap, time, copy, random, hashlib, json, os -from datetime import datetime, timedelta -from functools import wraps -from loguru import logger -from typing import * -from pathlib import Path -from io import BytesIO -from fastapi import Body, File, Form, Body, Query, UploadFile -from tempfile import SpooledTemporaryFile -import json - - -DATE_FORMAT = "%Y-%m-%d %H:%M:%S" - - -def getCurrentDatetime(): - return datetime.now().strftime("%Y-%m-%d %H:%M:00") - - -def addMinutesToTime(input_time: str, n: int = 5, dateformat=DATE_FORMAT): - dt = datetime.strptime(input_time, dateformat) - - # 前后加N分钟 - new_time_before = dt - timedelta(minutes=n) - new_time_after = dt + timedelta(minutes=n) - return new_time_before.strftime(dateformat), new_time_after.strftime(dateformat) - - -def timestampToDateformat(ts, interval=1000, dateformat=DATE_FORMAT): - '''将标准时间戳转换标准指定时间格式''' - return datetime.fromtimestamp(ts//interval).strftime(dateformat) - - -def datefromatToTimestamp(dt, interval=1000, dateformat=DATE_FORMAT): - '''将标准时间格式转换未标准时间戳''' - return datetime.strptime(dt, dateformat).timestamp()*interval - - -def func_timer(): - ''' - 用装饰器实现函数计时 - :param function: 需要计时的函数 - :return: None - ''' - @wraps(function) - def function_timer(*args, **kwargs): - t0 = time.time() - result = function(*args, **kwargs) - t1 = time.time() - logger.info('[Function: {name} finished, spent time: {time:.3f}s]'.format( - name=function.__name__, - time=t1 - t0 - )) - return result - return function_timer - - -def custom_serializer(obj): - try: - return str(obj) - except TypeError: - raise TypeError(f"Object of type {type(obj)} is not JSON serializable") - - -def read_jsonl_file(filename): - data = [] - with open(filename, "r", encoding="utf-8") as f: - for line in f: - data.append(json.loads(line)) - return data - - -def save_to_jsonl_file(data, filename): - dir_name = os.path.dirname(filename) - if not os.path.exists(dir_name): os.makedirs(dir_name) - - with open(filename, "w", encoding="utf-8") as f: - for item in data: - f.write(json.dumps(item, ensure_ascii=False) + "\n") - - -def read_json_file(filename): - with open(filename, "r", encoding="utf-8") as f: - return json.load(f) - - -def save_to_json_file(data, filename): - dir_name = os.path.dirname(filename) - if not os.path.exists(dir_name): os.makedirs(dir_name) - - with open(filename, "w", encoding="utf-8") as f: - json.dump(data, f, indent=2, ensure_ascii=False, default=custom_serializer) - - -def file_normalize(file: Union[str, Path, bytes], filename=None): - # logger.debug(f"{file}") - if isinstance(file, bytes): # raw bytes - file = BytesIO(file) - elif hasattr(file, "read"): # a file io like object - filename = filename or file.name - else: # a local path - file = Path(file).absolute().open("rb") - # logger.debug(file) - filename = filename or file.name - return file, filename - - -def get_uploadfile(file: Union[str, Path, bytes], filename=None) -> UploadFile: - temp_file = SpooledTemporaryFile(max_size=10 * 1024 * 1024) - temp_file.write(file.read()) - temp_file.seek(0) - return UploadFile(file=temp_file, filename=filename) - - diff --git a/coagent/utils/nebula_cp.sh b/coagent/utils/nebula_cp.sh deleted file mode 100644 index b98de0c..0000000 --- a/coagent/utils/nebula_cp.sh +++ /dev/null @@ -1,3 +0,0 @@ -if [ -d "/usr/local/nebula/data/meta" ]; then - cp -r /usr/local/nebula/data /home/user/chatbot/data/nebula_data -fi \ No newline at end of file diff --git a/coagent/utils/path_utils.py b/coagent/utils/path_utils.py deleted file mode 100644 index 5aec2fd..0000000 --- a/coagent/utils/path_utils.py +++ /dev/null @@ -1,71 +0,0 @@ -import os -from langchain.document_loaders import CSVLoader, PyPDFLoader, UnstructuredFileLoader, TextLoader, PythonLoader - -from coagent.retrieval.document_loaders import JSONLLoader, JSONLoader -# from configs.model_config import ( -# embedding_model_dict, -# KB_ROOT_PATH, -# ) -from loguru import logger - - -LOADERNAME2LOADER_DICT = { - "UnstructuredFileLoader": UnstructuredFileLoader, - "CSVLoader": CSVLoader, - "PyPDFLoader": PyPDFLoader, - "TextLoader": TextLoader, - "PythonLoader": PythonLoader, - "JSONLoader": JSONLoader, - "JSONLLoader": JSONLLoader -} - -LOADER2EXT_DICT = {"UnstructuredFileLoader": ['.eml', '.html', '.md', '.msg', '.rst', - '.rtf', '.xml', - '.doc', '.docx', '.epub', '.odt', - '.ppt', '.pptx', '.tsv'], - "CSVLoader": [".csv"], - "PyPDFLoader": [".pdf"], - "TextLoader": ['.txt'], - "PythonLoader": ['.py'], - "JSONLoader": ['.json'], - "JSONLLoader": ['.jsonl'] - } - -EXT2LOADER_DICT = {ext: LOADERNAME2LOADER_DICT[k] for k, exts in LOADER2EXT_DICT.items() for ext in exts} - -SUPPORTED_EXTS = [ext for sublist in LOADER2EXT_DICT.values() for ext in sublist] - - -def validate_kb_name(knowledge_base_id: str) -> bool: - # 检查是否包含预期外的字符或路径攻击关键字 - if "../" in knowledge_base_id: - return False - return True - -def get_kb_path(knowledge_base_name: str, kb_root_path: str): - return os.path.join(kb_root_path, knowledge_base_name) - -def get_doc_path(knowledge_base_name: str, kb_root_path: str): - return os.path.join(get_kb_path(knowledge_base_name, kb_root_path), "content") - -def get_vs_path(knowledge_base_name: str, kb_root_path: str): - return os.path.join(get_kb_path(knowledge_base_name, kb_root_path), "vector_store") - -def get_file_path(knowledge_base_name: str, doc_name: str, kb_root_path: str): - return os.path.join(get_doc_path(knowledge_base_name, kb_root_path), doc_name) - -def list_kbs_from_folder(kb_root_path: str): - return [f for f in os.listdir(kb_root_path) - if os.path.isdir(os.path.join(kb_root_path, f))] - -def list_docs_from_folder(kb_name: str, kb_root_path: str): - doc_path = get_doc_path(kb_name, kb_root_path) - if os.path.exists(doc_path): - return [file for file in os.listdir(doc_path) - if os.path.isfile(os.path.join(doc_path, file))] - return [] - -def get_LoaderClass(file_extension): - for LoaderClass, extensions in LOADER2EXT_DICT.items(): - if file_extension in extensions: - return LoaderClass \ No newline at end of file diff --git a/coagent/utils/postprocess.py b/coagent/utils/postprocess.py deleted file mode 100644 index fa65bc3..0000000 --- a/coagent/utils/postprocess.py +++ /dev/null @@ -1,12 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: postprocess.py -@time: 2023/11/9 下午4:01 -@desc: -''' -import html - -def replace_lt_gt(text: str): - text = html.unescape(text) - return text \ No newline at end of file diff --git a/coagent/utils/server_utils.py b/coagent/utils/server_utils.py deleted file mode 100644 index 926590d..0000000 --- a/coagent/utils/server_utils.py +++ /dev/null @@ -1,203 +0,0 @@ -import pydantic -from pydantic import BaseModel -from typing import List, Union -import torch -from fastapi import FastAPI -from pathlib import Path -import asyncio -from typing import Any, Optional -from loguru import logger - - -class BaseResponse(BaseModel): - code: int = pydantic.Field(200, description="API status code") - msg: str = pydantic.Field("success", description="API status message") - - class Config: - schema_extra = { - "example": { - "code": 200, - "msg": "success", - } - } - -class DataResponse(BaseResponse): - data: Union[str, bytes] = pydantic.Field(..., description="data") - - class Config: - schema_extra = { - "example": { - "code": 200, - "msg": "success", - "data": "data" - } - } - -class ListResponse(BaseResponse): - data: List[str] = pydantic.Field(..., description="List of names") - - class Config: - schema_extra = { - "example": { - "code": 200, - "msg": "success", - "data": ["doc1.docx", "doc2.pdf", "doc3.txt"], - } - } - - -class ChatMessage(BaseModel): - question: str = pydantic.Field(..., description="Question text") - response: str = pydantic.Field(..., description="Response text") - history: List[List[str]] = pydantic.Field(..., description="History text") - source_documents: List[str] = pydantic.Field( - ..., description="List of source documents and their scores" - ) - - class Config: - schema_extra = { - "example": { - "question": "工伤保险如何办理?", - "response": "根据已知信息,可以总结如下:\n\n1. 参保单位为员工缴纳工伤保险费,以保障员工在发生工伤时能够获得相应的待遇。\n" - "2. 不同地区的工伤保险缴费规定可能有所不同,需要向当地社保部门咨询以了解具体的缴费标准和规定。\n" - "3. 工伤从业人员及其近亲属需要申请工伤认定,确认享受的待遇资格,并按时缴纳工伤保险费。\n" - "4. 工伤保险待遇包括工伤医疗、康复、辅助器具配置费用、伤残待遇、工亡待遇、一次性工亡补助金等。\n" - "5. 工伤保险待遇领取资格认证包括长期待遇领取人员认证和一次性待遇领取人员认证。\n" - "6. 工伤保险基金支付的待遇项目包括工伤医疗待遇、康复待遇、辅助器具配置费用、一次性工亡补助金、丧葬补助金等。", - "history": [ - [ - "工伤保险是什么?", - "工伤保险是指用人单位按照国家规定,为本单位的职工和用人单位的其他人员,缴纳工伤保险费," - "由保险机构按照国家规定的标准,给予工伤保险待遇的社会保险制度。", - ] - ], - "source_documents": [ - "出处 [1] 广州市单位从业的特定人员参加工伤保险办事指引.docx:\n\n\t" - "( 一) 从业单位 (组织) 按“自愿参保”原则, 为未建 立劳动关系的特定从业人员单项参加工伤保险 、缴纳工伤保 险费。", - "出处 [2] ...", - "出处 [3] ...", - ], - } - } - -def torch_gc(): - if torch.cuda.is_available(): - # with torch.cuda.device(DEVICE): - torch.cuda.empty_cache() - torch.cuda.ipc_collect() - elif torch.backends.mps.is_available(): - try: - from torch.mps import empty_cache - empty_cache() - except Exception as e: - print(e) - print("如果您使用的是 macOS 建议将 pytorch 版本升级至 2.0.0 或更高版本,以支持及时清理 torch 产生的内存占用。") - - -def run_async(cor): - ''' - 在同步环境中运行异步代码. - ''' - try: - loop = asyncio.get_event_loop() - except: - loop = asyncio.new_event_loop() - return loop.run_until_complete(cor) - - -def iter_over_async(ait, loop): - ''' - 将异步生成器封装成同步生成器. - ''' - ait = ait.__aiter__() - async def get_next(): - try: - obj = await ait.__anext__() - return False, obj - except StopAsyncIteration: - return True, None - while True: - done, obj = loop.run_until_complete(get_next()) - if done: - break - yield obj - - - - -def MakeFastAPIOffline( - app: FastAPI, - static_dir = Path(__file__).parent / "static", - static_url = "/static-offline-docs", - docs_url: Optional[str] = "/docs", - redoc_url: Optional[str] = "/redoc", -) -> None: - """patch the FastAPI obj that doesn't rely on CDN for the documentation page""" - from fastapi import Request - from fastapi.openapi.docs import ( - get_redoc_html, - get_swagger_ui_html, - get_swagger_ui_oauth2_redirect_html, - ) - from fastapi.staticfiles import StaticFiles - from starlette.responses import HTMLResponse - - openapi_url = app.openapi_url - swagger_ui_oauth2_redirect_url = app.swagger_ui_oauth2_redirect_url - - def remove_route(url: str) -> None: - ''' - remove original route from app - ''' - index = None - for i, r in enumerate(app.routes): - if r.path.lower() == url.lower(): - index = i - break - if isinstance(index, int): - app.routes.pop(i) - - # Set up static file mount - app.mount( - static_url, - StaticFiles(directory=Path(static_dir).as_posix()), - name="static-offline-docs", - ) - - if docs_url is not None: - remove_route(docs_url) - remove_route(swagger_ui_oauth2_redirect_url) - - # Define the doc and redoc pages, pointing at the right files - @app.get(docs_url, include_in_schema=False) - async def custom_swagger_ui_html(request: Request) -> HTMLResponse: - root = request.scope.get("root_path") - favicon = f"{root}{static_url}/favicon.png" - return get_swagger_ui_html( - openapi_url=f"{root}{openapi_url}", - title=app.title + " - Swagger UI", - oauth2_redirect_url=swagger_ui_oauth2_redirect_url, - swagger_js_url=f"{root}{static_url}/swagger-ui-bundle.js", - swagger_css_url=f"{root}{static_url}/swagger-ui.css", - swagger_favicon_url=favicon, - ) - - @app.get(swagger_ui_oauth2_redirect_url, include_in_schema=False) - async def swagger_ui_redirect() -> HTMLResponse: - return get_swagger_ui_oauth2_redirect_html() - - if redoc_url is not None: - remove_route(redoc_url) - - @app.get(redoc_url, include_in_schema=False) - async def redoc_html(request: Request) -> HTMLResponse: - root = request.scope.get("root_path") - favicon = f"{root}{static_url}/favicon.png" - - return get_redoc_html( - openapi_url=f"{root}{openapi_url}", - title=app.title + " - ReDoc", - redoc_js_url=f"{root}{static_url}/redoc.standalone.js", - with_google_fonts=False, - redoc_favicon_url=favicon, - ) diff --git a/coagent/utils/static/__init__.py b/coagent/utils/static/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/coagent/utils/static/favicon.png b/coagent/utils/static/favicon.png deleted file mode 100644 index cf8f484..0000000 Binary files a/coagent/utils/static/favicon.png and /dev/null differ diff --git a/coagent/utils/static/redoc.standalone.js b/coagent/utils/static/redoc.standalone.js deleted file mode 100644 index 16e7a42..0000000 --- a/coagent/utils/static/redoc.standalone.js +++ /dev/null @@ -1,1782 +0,0 @@ -/*! For license information please see redoc.standalone.js.LICENSE.txt */ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):"function"==typeof define&&define.amd?define(["null"],t):"object"==typeof exports?exports.Redoc=t(require("null")):e.Redoc=t(e.null)}(this,(function(e){return function(){var t={5499:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CodeGen=t.Name=t.nil=t.stringify=t.str=t._=t.KeywordCxt=void 0;const r=n(3325),o=n(6479),i=n(5522),a=n(1603),s=["/properties"],l="http://json-schema.org/draft-07/schema";class c extends r.default{_addVocabularies(){super._addVocabularies(),o.default.forEach((e=>this.addVocabulary(e))),this.opts.discriminator&&this.addKeyword(i.default)}_addDefaultMetaSchema(){if(super._addDefaultMetaSchema(),!this.opts.meta)return;const e=this.opts.$data?this.$dataMetaSchema(a,s):a;this.addMetaSchema(e,l,!1),this.refs["http://json-schema.org/schema"]=l}defaultMeta(){return this.opts.defaultMeta=super.defaultMeta()||(this.getSchema(l)?l:void 0)}}e.exports=t=c,Object.defineProperty(t,"__esModule",{value:!0}),t.default=c;var u=n(1321);Object.defineProperty(t,"KeywordCxt",{enumerable:!0,get:function(){return u.KeywordCxt}});var p=n(4475);Object.defineProperty(t,"_",{enumerable:!0,get:function(){return p._}}),Object.defineProperty(t,"str",{enumerable:!0,get:function(){return p.str}}),Object.defineProperty(t,"stringify",{enumerable:!0,get:function(){return p.stringify}}),Object.defineProperty(t,"nil",{enumerable:!0,get:function(){return p.nil}}),Object.defineProperty(t,"Name",{enumerable:!0,get:function(){return p.Name}}),Object.defineProperty(t,"CodeGen",{enumerable:!0,get:function(){return p.CodeGen}})},4667:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.regexpCode=t.getProperty=t.safeStringify=t.stringify=t.strConcat=t.addCodeArg=t.str=t._=t.nil=t._Code=t.Name=t.IDENTIFIER=t._CodeOrName=void 0;class n{}t._CodeOrName=n,t.IDENTIFIER=/^[a-z$_][a-z$_0-9]*$/i;class r extends n{constructor(e){if(super(),!t.IDENTIFIER.test(e))throw new Error("CodeGen: name must be a valid identifier");this.str=e}toString(){return this.str}emptyStr(){return!1}get names(){return{[this.str]:1}}}t.Name=r;class o extends n{constructor(e){super(),this._items="string"==typeof e?[e]:e}toString(){return this.str}emptyStr(){if(this._items.length>1)return!1;const e=this._items[0];return""===e||'""'===e}get str(){var e;return null!==(e=this._str)&&void 0!==e?e:this._str=this._items.reduce(((e,t)=>`${e}${t}`),"")}get names(){var e;return null!==(e=this._names)&&void 0!==e?e:this._names=this._items.reduce(((e,t)=>(t instanceof r&&(e[t.str]=(e[t.str]||0)+1),e)),{})}}function i(e,...t){const n=[e[0]];let r=0;for(;r"),GTE:new r._Code(">="),LT:new r._Code("<"),LTE:new r._Code("<="),EQ:new r._Code("==="),NEQ:new r._Code("!=="),NOT:new r._Code("!"),OR:new r._Code("||"),AND:new r._Code("&&"),ADD:new r._Code("+")};class s{optimizeNodes(){return this}optimizeNames(e,t){return this}}class l extends s{constructor(e,t,n){super(),this.varKind=e,this.name=t,this.rhs=n}render({es5:e,_n:t}){const n=e?o.varKinds.var:this.varKind,r=void 0===this.rhs?"":` = ${this.rhs}`;return`${n} ${this.name}${r};`+t}optimizeNames(e,t){if(e[this.name.str])return this.rhs&&(this.rhs=R(this.rhs,e,t)),this}get names(){return this.rhs instanceof r._CodeOrName?this.rhs.names:{}}}class c extends s{constructor(e,t,n){super(),this.lhs=e,this.rhs=t,this.sideEffects=n}render({_n:e}){return`${this.lhs} = ${this.rhs};`+e}optimizeNames(e,t){if(!(this.lhs instanceof r.Name)||e[this.lhs.str]||this.sideEffects)return this.rhs=R(this.rhs,e,t),this}get names(){return C(this.lhs instanceof r.Name?{}:{...this.lhs.names},this.rhs)}}class u extends c{constructor(e,t,n,r){super(e,n,r),this.op=t}render({_n:e}){return`${this.lhs} ${this.op}= ${this.rhs};`+e}}class p extends s{constructor(e){super(),this.label=e,this.names={}}render({_n:e}){return`${this.label}:`+e}}class d extends s{constructor(e){super(),this.label=e,this.names={}}render({_n:e}){return`break${this.label?` ${this.label}`:""};`+e}}class f extends s{constructor(e){super(),this.error=e}render({_n:e}){return`throw ${this.error};`+e}get names(){return this.error.names}}class h extends s{constructor(e){super(),this.code=e}render({_n:e}){return`${this.code};`+e}optimizeNodes(){return`${this.code}`?this:void 0}optimizeNames(e,t){return this.code=R(this.code,e,t),this}get names(){return this.code instanceof r._CodeOrName?this.code.names:{}}}class m extends s{constructor(e=[]){super(),this.nodes=e}render(e){return this.nodes.reduce(((t,n)=>t+n.render(e)),"")}optimizeNodes(){const{nodes:e}=this;let t=e.length;for(;t--;){const n=e[t].optimizeNodes();Array.isArray(n)?e.splice(t,1,...n):n?e[t]=n:e.splice(t,1)}return e.length>0?this:void 0}optimizeNames(e,t){const{nodes:n}=this;let r=n.length;for(;r--;){const o=n[r];o.optimizeNames(e,t)||(j(e,o.names),n.splice(r,1))}return n.length>0?this:void 0}get names(){return this.nodes.reduce(((e,t)=>$(e,t.names)),{})}}class g extends m{render(e){return"{"+e._n+super.render(e)+"}"+e._n}}class y extends m{}class v extends g{}v.kind="else";class b extends g{constructor(e,t){super(t),this.condition=e}render(e){let t=`if(${this.condition})`+super.render(e);return this.else&&(t+="else "+this.else.render(e)),t}optimizeNodes(){super.optimizeNodes();const e=this.condition;if(!0===e)return this.nodes;let t=this.else;if(t){const e=t.optimizeNodes();t=this.else=Array.isArray(e)?new v(e):e}return t?!1===e?t instanceof b?t:t.nodes:this.nodes.length?this:new b(T(e),t instanceof b?[t]:t.nodes):!1!==e&&this.nodes.length?this:void 0}optimizeNames(e,t){var n;if(this.else=null===(n=this.else)||void 0===n?void 0:n.optimizeNames(e,t),super.optimizeNames(e,t)||this.else)return this.condition=R(this.condition,e,t),this}get names(){const e=super.names;return C(e,this.condition),this.else&&$(e,this.else.names),e}}b.kind="if";class w extends g{}w.kind="for";class x extends w{constructor(e){super(),this.iteration=e}render(e){return`for(${this.iteration})`+super.render(e)}optimizeNames(e,t){if(super.optimizeNames(e,t))return this.iteration=R(this.iteration,e,t),this}get names(){return $(super.names,this.iteration.names)}}class k extends w{constructor(e,t,n,r){super(),this.varKind=e,this.name=t,this.from=n,this.to=r}render(e){const t=e.es5?o.varKinds.var:this.varKind,{name:n,from:r,to:i}=this;return`for(${t} ${n}=${r}; ${n}<${i}; ${n}++)`+super.render(e)}get names(){const e=C(super.names,this.from);return C(e,this.to)}}class _ extends w{constructor(e,t,n,r){super(),this.loop=e,this.varKind=t,this.name=n,this.iterable=r}render(e){return`for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})`+super.render(e)}optimizeNames(e,t){if(super.optimizeNames(e,t))return this.iterable=R(this.iterable,e,t),this}get names(){return $(super.names,this.iterable.names)}}class O extends g{constructor(e,t,n){super(),this.name=e,this.args=t,this.async=n}render(e){return`${this.async?"async ":""}function ${this.name}(${this.args})`+super.render(e)}}O.kind="func";class S extends m{render(e){return"return "+super.render(e)}}S.kind="return";class E extends g{render(e){let t="try"+super.render(e);return this.catch&&(t+=this.catch.render(e)),this.finally&&(t+=this.finally.render(e)),t}optimizeNodes(){var e,t;return super.optimizeNodes(),null===(e=this.catch)||void 0===e||e.optimizeNodes(),null===(t=this.finally)||void 0===t||t.optimizeNodes(),this}optimizeNames(e,t){var n,r;return super.optimizeNames(e,t),null===(n=this.catch)||void 0===n||n.optimizeNames(e,t),null===(r=this.finally)||void 0===r||r.optimizeNames(e,t),this}get names(){const e=super.names;return this.catch&&$(e,this.catch.names),this.finally&&$(e,this.finally.names),e}}class P extends g{constructor(e){super(),this.error=e}render(e){return`catch(${this.error})`+super.render(e)}}P.kind="catch";class A extends g{render(e){return"finally"+super.render(e)}}function $(e,t){for(const n in t)e[n]=(e[n]||0)+(t[n]||0);return e}function C(e,t){return t instanceof r._CodeOrName?$(e,t.names):e}function R(e,t,n){return e instanceof r.Name?i(e):(o=e)instanceof r._Code&&o._items.some((e=>e instanceof r.Name&&1===t[e.str]&&void 0!==n[e.str]))?new r._Code(e._items.reduce(((e,t)=>(t instanceof r.Name&&(t=i(t)),t instanceof r._Code?e.push(...t._items):e.push(t),e)),[])):e;var o;function i(e){const r=n[e.str];return void 0===r||1!==t[e.str]?e:(delete t[e.str],r)}}function j(e,t){for(const n in t)e[n]=(e[n]||0)-(t[n]||0)}function T(e){return"boolean"==typeof e||"number"==typeof e||null===e?!e:r._`!${L(e)}`}A.kind="finally",t.CodeGen=class{constructor(e,t={}){this._values={},this._blockStarts=[],this._constants={},this.opts={...t,_n:t.lines?"\n":""},this._extScope=e,this._scope=new o.Scope({parent:e}),this._nodes=[new y]}toString(){return this._root.render(this.opts)}name(e){return this._scope.name(e)}scopeName(e){return this._extScope.name(e)}scopeValue(e,t){const n=this._extScope.value(e,t);return(this._values[n.prefix]||(this._values[n.prefix]=new Set)).add(n),n}getScopeValue(e,t){return this._extScope.getValue(e,t)}scopeRefs(e){return this._extScope.scopeRefs(e,this._values)}scopeCode(){return this._extScope.scopeCode(this._values)}_def(e,t,n,r){const o=this._scope.toName(t);return void 0!==n&&r&&(this._constants[o.str]=n),this._leafNode(new l(e,o,n)),o}const(e,t,n){return this._def(o.varKinds.const,e,t,n)}let(e,t,n){return this._def(o.varKinds.let,e,t,n)}var(e,t,n){return this._def(o.varKinds.var,e,t,n)}assign(e,t,n){return this._leafNode(new c(e,t,n))}add(e,n){return this._leafNode(new u(e,t.operators.ADD,n))}code(e){return"function"==typeof e?e():e!==r.nil&&this._leafNode(new h(e)),this}object(...e){const t=["{"];for(const[n,o]of e)t.length>1&&t.push(","),t.push(n),(n!==o||this.opts.es5)&&(t.push(":"),r.addCodeArg(t,o));return t.push("}"),new r._Code(t)}if(e,t,n){if(this._blockNode(new b(e)),t&&n)this.code(t).else().code(n).endIf();else if(t)this.code(t).endIf();else if(n)throw new Error('CodeGen: "else" body without "then" body');return this}elseIf(e){return this._elseNode(new b(e))}else(){return this._elseNode(new v)}endIf(){return this._endBlockNode(b,v)}_for(e,t){return this._blockNode(e),t&&this.code(t).endFor(),this}for(e,t){return this._for(new x(e),t)}forRange(e,t,n,r,i=(this.opts.es5?o.varKinds.var:o.varKinds.let)){const a=this._scope.toName(e);return this._for(new k(i,a,t,n),(()=>r(a)))}forOf(e,t,n,i=o.varKinds.const){const a=this._scope.toName(e);if(this.opts.es5){const e=t instanceof r.Name?t:this.var("_arr",t);return this.forRange("_i",0,r._`${e}.length`,(t=>{this.var(a,r._`${e}[${t}]`),n(a)}))}return this._for(new _("of",i,a,t),(()=>n(a)))}forIn(e,t,n,i=(this.opts.es5?o.varKinds.var:o.varKinds.const)){if(this.opts.ownProperties)return this.forOf(e,r._`Object.keys(${t})`,n);const a=this._scope.toName(e);return this._for(new _("in",i,a,t),(()=>n(a)))}endFor(){return this._endBlockNode(w)}label(e){return this._leafNode(new p(e))}break(e){return this._leafNode(new d(e))}return(e){const t=new S;if(this._blockNode(t),this.code(e),1!==t.nodes.length)throw new Error('CodeGen: "return" should have one node');return this._endBlockNode(S)}try(e,t,n){if(!t&&!n)throw new Error('CodeGen: "try" without "catch" and "finally"');const r=new E;if(this._blockNode(r),this.code(e),t){const e=this.name("e");this._currNode=r.catch=new P(e),t(e)}return n&&(this._currNode=r.finally=new A,this.code(n)),this._endBlockNode(P,A)}throw(e){return this._leafNode(new f(e))}block(e,t){return this._blockStarts.push(this._nodes.length),e&&this.code(e).endBlock(t),this}endBlock(e){const t=this._blockStarts.pop();if(void 0===t)throw new Error("CodeGen: not in self-balancing block");const n=this._nodes.length-t;if(n<0||void 0!==e&&n!==e)throw new Error(`CodeGen: wrong number of nodes: ${n} vs ${e} expected`);return this._nodes.length=t,this}func(e,t=r.nil,n,o){return this._blockNode(new O(e,t,n)),o&&this.code(o).endFunc(),this}endFunc(){return this._endBlockNode(O)}optimize(e=1){for(;e-- >0;)this._root.optimizeNodes(),this._root.optimizeNames(this._root.names,this._constants)}_leafNode(e){return this._currNode.nodes.push(e),this}_blockNode(e){this._currNode.nodes.push(e),this._nodes.push(e)}_endBlockNode(e,t){const n=this._currNode;if(n instanceof e||t&&n instanceof t)return this._nodes.pop(),this;throw new Error(`CodeGen: not in block "${t?`${e.kind}/${t.kind}`:e.kind}"`)}_elseNode(e){const t=this._currNode;if(!(t instanceof b))throw new Error('CodeGen: "else" without "if"');return this._currNode=t.else=e,this}get _root(){return this._nodes[0]}get _currNode(){const e=this._nodes;return e[e.length-1]}set _currNode(e){const t=this._nodes;t[t.length-1]=e}},t.not=T;const I=D(t.operators.AND);t.and=function(...e){return e.reduce(I)};const N=D(t.operators.OR);function D(e){return(t,n)=>t===r.nil?n:n===r.nil?t:r._`${L(t)} ${e} ${L(n)}`}function L(e){return e instanceof r.Name?e:r._`(${e})`}t.or=function(...e){return e.reduce(N)}},7791:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ValueScope=t.ValueScopeName=t.Scope=t.varKinds=t.UsedValueState=void 0;const r=n(4667);class o extends Error{constructor(e){super(`CodeGen: "code" for ${e} not defined`),this.value=e.value}}var i;!function(e){e[e.Started=0]="Started",e[e.Completed=1]="Completed"}(i=t.UsedValueState||(t.UsedValueState={})),t.varKinds={const:new r.Name("const"),let:new r.Name("let"),var:new r.Name("var")};class a{constructor({prefixes:e,parent:t}={}){this._names={},this._prefixes=e,this._parent=t}toName(e){return e instanceof r.Name?e:this.name(e)}name(e){return new r.Name(this._newName(e))}_newName(e){return`${e}${(this._names[e]||this._nameGroup(e)).index++}`}_nameGroup(e){var t,n;if((null===(n=null===(t=this._parent)||void 0===t?void 0:t._prefixes)||void 0===n?void 0:n.has(e))||this._prefixes&&!this._prefixes.has(e))throw new Error(`CodeGen: prefix "${e}" is not allowed in this scope`);return this._names[e]={prefix:e,index:0}}}t.Scope=a;class s extends r.Name{constructor(e,t){super(t),this.prefix=e}setValue(e,{property:t,itemIndex:n}){this.value=e,this.scopePath=r._`.${new r.Name(t)}[${n}]`}}t.ValueScopeName=s;const l=r._`\n`;t.ValueScope=class extends a{constructor(e){super(e),this._values={},this._scope=e.scope,this.opts={...e,_n:e.lines?l:r.nil}}get(){return this._scope}name(e){return new s(e,this._newName(e))}value(e,t){var n;if(void 0===t.ref)throw new Error("CodeGen: ref must be passed in value");const r=this.toName(e),{prefix:o}=r,i=null!==(n=t.key)&&void 0!==n?n:t.ref;let a=this._values[o];if(a){const e=a.get(i);if(e)return e}else a=this._values[o]=new Map;a.set(i,r);const s=this._scope[o]||(this._scope[o]=[]),l=s.length;return s[l]=t.ref,r.setValue(t,{property:o,itemIndex:l}),r}getValue(e,t){const n=this._values[e];if(n)return n.get(t)}scopeRefs(e,t=this._values){return this._reduceValues(t,(t=>{if(void 0===t.scopePath)throw new Error(`CodeGen: name "${t}" has no value`);return r._`${e}${t.scopePath}`}))}scopeCode(e=this._values,t,n){return this._reduceValues(e,(e=>{if(void 0===e.value)throw new Error(`CodeGen: name "${e}" has no value`);return e.value.code}),t,n)}_reduceValues(e,n,a={},s){let l=r.nil;for(const c in e){const u=e[c];if(!u)continue;const p=a[c]=a[c]||new Map;u.forEach((e=>{if(p.has(e))return;p.set(e,i.Started);let a=n(e);if(a){const n=this.opts.es5?t.varKinds.var:t.varKinds.const;l=r._`${l}${n} ${e} = ${a};${this.opts._n}`}else{if(!(a=null==s?void 0:s(e)))throw new o(e);l=r._`${l}${a}${this.opts._n}`}p.set(e,i.Completed)}))}return l}}},1885:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.extendErrors=t.resetErrorsCount=t.reportExtraError=t.reportError=t.keyword$DataError=t.keywordError=void 0;const r=n(4475),o=n(6124),i=n(5018);function a(e,t){const n=e.const("err",t);e.if(r._`${i.default.vErrors} === null`,(()=>e.assign(i.default.vErrors,r._`[${n}]`)),r._`${i.default.vErrors}.push(${n})`),e.code(r._`${i.default.errors}++`)}function s(e,t){const{gen:n,validateName:o,schemaEnv:i}=e;i.$async?n.throw(r._`new ${e.ValidationError}(${t})`):(n.assign(r._`${o}.errors`,t),n.return(!1))}t.keywordError={message:({keyword:e})=>r.str`should pass "${e}" keyword validation`},t.keyword$DataError={message:({keyword:e,schemaType:t})=>t?r.str`"${e}" keyword must be ${t} ($data)`:r.str`"${e}" keyword is invalid ($data)`},t.reportError=function(e,n=t.keywordError,o,i){const{it:l}=e,{gen:u,compositeRule:p,allErrors:d}=l,f=c(e,n,o);(null!=i?i:p||d)?a(u,f):s(l,r._`[${f}]`)},t.reportExtraError=function(e,n=t.keywordError,r){const{it:o}=e,{gen:l,compositeRule:u,allErrors:p}=o;a(l,c(e,n,r)),u||p||s(o,i.default.vErrors)},t.resetErrorsCount=function(e,t){e.assign(i.default.errors,t),e.if(r._`${i.default.vErrors} !== null`,(()=>e.if(t,(()=>e.assign(r._`${i.default.vErrors}.length`,t)),(()=>e.assign(i.default.vErrors,null)))))},t.extendErrors=function({gen:e,keyword:t,schemaValue:n,data:o,errsCount:a,it:s}){if(void 0===a)throw new Error("ajv implementation error");const l=e.name("err");e.forRange("i",a,i.default.errors,(a=>{e.const(l,r._`${i.default.vErrors}[${a}]`),e.if(r._`${l}.instancePath === undefined`,(()=>e.assign(r._`${l}.instancePath`,r.strConcat(i.default.instancePath,s.errorPath)))),e.assign(r._`${l}.schemaPath`,r.str`${s.errSchemaPath}/${t}`),s.opts.verbose&&(e.assign(r._`${l}.schema`,n),e.assign(r._`${l}.data`,o))}))};const l={keyword:new r.Name("keyword"),schemaPath:new r.Name("schemaPath"),params:new r.Name("params"),propertyName:new r.Name("propertyName"),message:new r.Name("message"),schema:new r.Name("schema"),parentSchema:new r.Name("parentSchema")};function c(e,t,n){const{createErrors:o}=e.it;return!1===o?r._`{}`:function(e,t,n={}){const{gen:o,it:a}=e,s=[u(a,n),p(e,n)];return function(e,{params:t,message:n},o){const{keyword:a,data:s,schemaValue:c,it:u}=e,{opts:p,propertyName:d,topSchemaRef:f,schemaPath:h}=u;o.push([l.keyword,a],[l.params,"function"==typeof t?t(e):t||r._`{}`]),p.messages&&o.push([l.message,"function"==typeof n?n(e):n]),p.verbose&&o.push([l.schema,c],[l.parentSchema,r._`${f}${h}`],[i.default.data,s]),d&&o.push([l.propertyName,d])}(e,t,s),o.object(...s)}(e,t,n)}function u({errorPath:e},{instancePath:t}){const n=t?r.str`${e}${o.getErrorPath(t,o.Type.Str)}`:e;return[i.default.instancePath,r.strConcat(i.default.instancePath,n)]}function p({keyword:e,it:{errSchemaPath:t}},{schemaPath:n,parentSchema:i}){let a=i?t:r.str`${t}/${e}`;return n&&(a=r.str`${a}${o.getErrorPath(n,o.Type.Str)}`),[l.schemaPath,a]}},7805:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.resolveSchema=t.getCompilingSchema=t.resolveRef=t.compileSchema=t.SchemaEnv=void 0;const r=n(4475),o=n(8451),i=n(5018),a=n(9826),s=n(6124),l=n(1321),c=n(540);class u{constructor(e){var t;let n;this.refs={},this.dynamicAnchors={},"object"==typeof e.schema&&(n=e.schema),this.schema=e.schema,this.schemaId=e.schemaId,this.root=e.root||this,this.baseId=null!==(t=e.baseId)&&void 0!==t?t:a.normalizeId(null==n?void 0:n[e.schemaId||"$id"]),this.schemaPath=e.schemaPath,this.localRefs=e.localRefs,this.meta=e.meta,this.$async=null==n?void 0:n.$async,this.refs={}}}function p(e){const t=f.call(this,e);if(t)return t;const n=a.getFullPath(e.root.baseId),{es5:s,lines:c}=this.opts.code,{ownProperties:u}=this.opts,p=new r.CodeGen(this.scope,{es5:s,lines:c,ownProperties:u});let d;e.$async&&(d=p.scopeValue("Error",{ref:o.default,code:r._`require("ajv/dist/runtime/validation_error").default`}));const h=p.scopeName("validate");e.validateName=h;const m={gen:p,allErrors:this.opts.allErrors,data:i.default.data,parentData:i.default.parentData,parentDataProperty:i.default.parentDataProperty,dataNames:[i.default.data],dataPathArr:[r.nil],dataLevel:0,dataTypes:[],definedProperties:new Set,topSchemaRef:p.scopeValue("schema",!0===this.opts.code.source?{ref:e.schema,code:r.stringify(e.schema)}:{ref:e.schema}),validateName:h,ValidationError:d,schema:e.schema,schemaEnv:e,rootId:n,baseId:e.baseId||n,schemaPath:r.nil,errSchemaPath:e.schemaPath||(this.opts.jtd?"":"#"),errorPath:r._`""`,opts:this.opts,self:this};let g;try{this._compilations.add(e),l.validateFunctionCode(m),p.optimize(this.opts.code.optimize);const t=p.toString();g=`const visitedNodesForRef = new WeakMap(); ${p.scopeRefs(i.default.scope)}return ${t}`,this.opts.code.process&&(g=this.opts.code.process(g,e));const n=new Function(`${i.default.self}`,`${i.default.scope}`,g)(this,this.scope.get());if(this.scope.value(h,{ref:n}),n.errors=null,n.schema=e.schema,n.schemaEnv=e,e.$async&&(n.$async=!0),!0===this.opts.code.source&&(n.source={validateName:h,validateCode:t,scopeValues:p._values}),this.opts.unevaluated){const{props:e,items:t}=m;n.evaluated={props:e instanceof r.Name?void 0:e,items:t instanceof r.Name?void 0:t,dynamicProps:e instanceof r.Name,dynamicItems:t instanceof r.Name},n.source&&(n.source.evaluated=r.stringify(n.evaluated))}return e.validate=n,e}catch(t){throw delete e.validate,delete e.validateName,g&&this.logger.error("Error compiling schema, function code:",g),t}finally{this._compilations.delete(e)}}function d(e){return a.inlineRef(e.schema,this.opts.inlineRefs)?e.schema:e.validate?e:p.call(this,e)}function f(e){for(const r of this._compilations)if(n=e,(t=r).schema===n.schema&&t.root===n.root&&t.baseId===n.baseId)return r;var t,n}function h(e,t){let n;for(;"string"==typeof(n=this.refs[t]);)t=n;return n||this.schemas[t]||m.call(this,e,t)}function m(e,t){const n=c.parse(t),r=a._getFullPath(n);let o=a.getFullPath(e.baseId);if(Object.keys(e.schema).length>0&&r===o)return y.call(this,n,e);const i=a.normalizeId(r),s=this.refs[i]||this.schemas[i];if("string"==typeof s){const t=m.call(this,e,s);if("object"!=typeof(null==t?void 0:t.schema))return;return y.call(this,n,t)}if("object"==typeof(null==s?void 0:s.schema)){if(s.validate||p.call(this,s),i===a.normalizeId(t)){const{schema:t}=s,{schemaId:n}=this.opts,r=t[n];return r&&(o=a.resolveUrl(o,r)),new u({schema:t,schemaId:n,root:e,baseId:o})}return y.call(this,n,s)}}t.SchemaEnv=u,t.compileSchema=p,t.resolveRef=function(e,t,n){var r;const o=a.resolveUrl(t,n),i=e.refs[o];if(i)return i;let s=h.call(this,e,o);if(void 0===s){const n=null===(r=e.localRefs)||void 0===r?void 0:r[o],{schemaId:i}=this.opts;n&&(s=new u({schema:n,schemaId:i,root:e,baseId:t}))}if(void 0===s&&this.opts.loadSchemaSync){const r=this.opts.loadSchemaSync(t,n,o);!r||this.refs[o]||this.schemas[o]||(this.addSchema(r,o,void 0),s=h.call(this,e,o))}return void 0!==s?e.refs[o]=d.call(this,s):void 0},t.getCompilingSchema=f,t.resolveSchema=m;const g=new Set(["properties","patternProperties","enum","dependencies","definitions"]);function y(e,{baseId:t,schema:n,root:r}){var o;if("/"!==(null===(o=e.fragment)||void 0===o?void 0:o[0]))return;for(const r of e.fragment.slice(1).split("/")){if("boolean"==typeof n)return;if(void 0===(n=n[s.unescapeFragment(r)]))return;const e="object"==typeof n&&n[this.opts.schemaId];!g.has(r)&&e&&(t=a.resolveUrl(t,e))}let i;if("boolean"!=typeof n&&n.$ref&&!s.schemaHasRulesButRef(n,this.RULES)){const e=a.resolveUrl(t,n.$ref);i=m.call(this,r,e)}const{schemaId:l}=this.opts;return i=i||new u({schema:n,schemaId:l,root:r,baseId:t}),i.schema!==i.root.schema?i:void 0}},5018:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4475),o={data:new r.Name("data"),valCxt:new r.Name("valCxt"),instancePath:new r.Name("instancePath"),parentData:new r.Name("parentData"),parentDataProperty:new r.Name("parentDataProperty"),rootData:new r.Name("rootData"),dynamicAnchors:new r.Name("dynamicAnchors"),vErrors:new r.Name("vErrors"),errors:new r.Name("errors"),this:new r.Name("this"),self:new r.Name("self"),scope:new r.Name("scope"),json:new r.Name("json"),jsonPos:new r.Name("jsonPos"),jsonLen:new r.Name("jsonLen"),jsonPart:new r.Name("jsonPart")};t.default=o},4143:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(9826);class o extends Error{constructor(e,t,n){super(n||`can't resolve reference ${t} from id ${e}`),this.missingRef=r.resolveUrl(e,t),this.missingSchema=r.normalizeId(r.getFullPath(this.missingRef))}}t.default=o},9826:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getSchemaRefs=t.resolveUrl=t.normalizeId=t._getFullPath=t.getFullPath=t.inlineRef=void 0;const r=n(6124),o=n(4063),i=n(4029),a=n(540),s=new Set(["type","format","pattern","maxLength","minLength","maxProperties","minProperties","maxItems","minItems","maximum","minimum","uniqueItems","multipleOf","required","enum","const"]);t.inlineRef=function(e,t=!0){return"boolean"==typeof e||(!0===t?!c(e):!!t&&u(e)<=t)};const l=new Set(["$ref","$recursiveRef","$recursiveAnchor","$dynamicRef","$dynamicAnchor"]);function c(e){for(const t in e){if(l.has(t))return!0;const n=e[t];if(Array.isArray(n)&&n.some(c))return!0;if("object"==typeof n&&c(n))return!0}return!1}function u(e){let t=0;for(const n in e){if("$ref"===n)return 1/0;if(t++,!s.has(n)&&("object"==typeof e[n]&&r.eachItem(e[n],(e=>t+=u(e))),t===1/0))return 1/0}return t}function p(e="",t){return!1!==t&&(e=h(e)),d(a.parse(e))}function d(e){return a.serialize(e).split("#")[0]+"#"}t.getFullPath=p,t._getFullPath=d;const f=/#\/?$/;function h(e){return e?e.replace(f,""):""}t.normalizeId=h,t.resolveUrl=function(e,t){return t=h(t),a.resolve(e,t)};const m=/^[a-z_][-a-z0-9._]*$/i;t.getSchemaRefs=function(e){if("boolean"==typeof e)return{};const{schemaId:t}=this.opts,n=h(e[t]),r={"":n},s=p(n,!1),l={},c=new Set;return i(e,{allKeys:!0},((e,n,o,i)=>{if(void 0===i)return;const p=s+n;let f=r[i];function g(t){if(t=h(f?a.resolve(f,t):t),c.has(t))throw d(t);c.add(t);let n=this.refs[t];return"string"==typeof n&&(n=this.refs[n]),"object"==typeof n?u(e,n.schema,t):t!==h(p)&&("#"===t[0]?(u(e,l[t],t),l[t]=e):this.refs[t]=p),t}function y(e){if("string"==typeof e){if(!m.test(e))throw new Error(`invalid anchor "${e}"`);g.call(this,`#${e}`)}}"string"==typeof e[t]&&(f=g.call(this,e[t])),y.call(this,e.$anchor),y.call(this,e.$dynamicAnchor),r[n]=f})),l;function u(e,t,n){if(void 0!==t&&!o(e,t))throw d(n)}function d(e){return new Error(`reference "${e}" resolves to more than one schema`)}}},3664:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getRules=t.isJSONType=void 0;const n=new Set(["string","number","integer","boolean","null","object","array"]);t.isJSONType=function(e){return"string"==typeof e&&n.has(e)},t.getRules=function(){const e={number:{type:"number",rules:[]},string:{type:"string",rules:[]},array:{type:"array",rules:[]},object:{type:"object",rules:[]}};return{types:{...e,integer:!0,boolean:!0,null:!0},rules:[{rules:[]},e.number,e.string,e.array,e.object],post:{rules:[]},all:{},keywords:{}}}},6124:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.checkStrictMode=t.getErrorPath=t.Type=t.useFunc=t.setEvaluated=t.evaluatedPropsToName=t.mergeEvaluated=t.eachItem=t.unescapeJsonPointer=t.escapeJsonPointer=t.escapeFragment=t.unescapeFragment=t.schemaRefOrVal=t.schemaHasRulesButRef=t.schemaHasRules=t.checkUnknownRules=t.alwaysValidSchema=t.toHash=void 0;const r=n(4475),o=n(4667);function i(e,t=e.schema){const{opts:n,self:r}=e;if(!n.strictSchema)return;if("boolean"==typeof t)return;const o=r.RULES.keywords;for(const n in t)o[n]||h(e,`unknown keyword: "${n}"`)}function a(e,t){if("boolean"==typeof e)return!e;for(const n in e)if(t[n])return!0;return!1}function s(e){return"number"==typeof e?`${e}`:e.replace(/~/g,"~0").replace(/\//g,"~1")}function l(e){return e.replace(/~1/g,"/").replace(/~0/g,"~")}function c({mergeNames:e,mergeToName:t,mergeValues:n,resultToName:o}){return(i,a,s,l)=>{const c=void 0===s?a:s instanceof r.Name?(a instanceof r.Name?e(i,a,s):t(i,a,s),s):a instanceof r.Name?(t(i,s,a),a):n(a,s);return l!==r.Name||c instanceof r.Name?c:o(i,c)}}function u(e,t){if(!0===t)return e.var("props",!0);const n=e.var("props",r._`{}`);return void 0!==t&&p(e,n,t),n}function p(e,t,n){Object.keys(n).forEach((n=>e.assign(r._`${t}${r.getProperty(n)}`,!0)))}t.toHash=function(e){const t={};for(const n of e)t[n]=!0;return t},t.alwaysValidSchema=function(e,t){return"boolean"==typeof t?t:0===Object.keys(t).length||(i(e,t),!a(t,e.self.RULES.all))},t.checkUnknownRules=i,t.schemaHasRules=a,t.schemaHasRulesButRef=function(e,t){if("boolean"==typeof e)return!e;for(const n in e)if("$ref"!==n&&t.all[n])return!0;return!1},t.schemaRefOrVal=function({topSchemaRef:e,schemaPath:t},n,o,i){if(!i){if("number"==typeof n||"boolean"==typeof n)return n;if("string"==typeof n)return r._`${n}`}return r._`${e}${t}${r.getProperty(o)}`},t.unescapeFragment=function(e){return l(decodeURIComponent(e))},t.escapeFragment=function(e){return encodeURIComponent(s(e))},t.escapeJsonPointer=s,t.unescapeJsonPointer=l,t.eachItem=function(e,t){if(Array.isArray(e))for(const n of e)t(n);else t(e)},t.mergeEvaluated={props:c({mergeNames:(e,t,n)=>e.if(r._`${n} !== true && ${t} !== undefined`,(()=>{e.if(r._`${t} === true`,(()=>e.assign(n,!0)),(()=>e.assign(n,r._`${n} || {}`).code(r._`Object.assign(${n}, ${t})`)))})),mergeToName:(e,t,n)=>e.if(r._`${n} !== true`,(()=>{!0===t?e.assign(n,!0):(e.assign(n,r._`${n} || {}`),p(e,n,t))})),mergeValues:(e,t)=>!0===e||{...e,...t},resultToName:u}),items:c({mergeNames:(e,t,n)=>e.if(r._`${n} !== true && ${t} !== undefined`,(()=>e.assign(n,r._`${t} === true ? true : ${n} > ${t} ? ${n} : ${t}`))),mergeToName:(e,t,n)=>e.if(r._`${n} !== true`,(()=>e.assign(n,!0===t||r._`${n} > ${t} ? ${n} : ${t}`))),mergeValues:(e,t)=>!0===e||Math.max(e,t),resultToName:(e,t)=>e.var("items",t)})},t.evaluatedPropsToName=u,t.setEvaluated=p;const d={};var f;function h(e,t,n=e.opts.strictSchema){if(n){if(t=`strict mode: ${t}`,!0===n)throw new Error(t);e.self.logger.warn(t)}}t.useFunc=function(e,t){return e.scopeValue("func",{ref:t,code:d[t.code]||(d[t.code]=new o._Code(t.code))})},function(e){e[e.Num=0]="Num",e[e.Str=1]="Str"}(f=t.Type||(t.Type={})),t.getErrorPath=function(e,t,n){if(e instanceof r.Name){const o=t===f.Num;return n?o?r._`"[" + ${e} + "]"`:r._`"['" + ${e} + "']"`:o?r._`"/" + ${e}`:r._`"/" + ${e}.replace(/~/g, "~0").replace(/\\//g, "~1")`}return n?r.getProperty(e).toString():"/"+s(e)},t.checkStrictMode=h},4566:function(e,t){"use strict";function n(e,t){return t.rules.some((t=>r(e,t)))}function r(e,t){var n;return void 0!==e[t.keyword]||(null===(n=t.definition.implements)||void 0===n?void 0:n.some((t=>void 0!==e[t])))}Object.defineProperty(t,"__esModule",{value:!0}),t.shouldUseRule=t.shouldUseGroup=t.schemaHasRulesForType=void 0,t.schemaHasRulesForType=function({schema:e,self:t},r){const o=t.RULES.types[r];return o&&!0!==o&&n(e,o)},t.shouldUseGroup=n,t.shouldUseRule=r},7627:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.boolOrEmptySchema=t.topBoolOrEmptySchema=void 0;const r=n(1885),o=n(4475),i=n(5018),a={message:"boolean schema is false"};function s(e,t){const{gen:n,data:o}=e,i={gen:n,keyword:"false schema",data:o,schema:!1,schemaCode:!1,schemaValue:!1,params:{},it:e};r.reportError(i,a,void 0,t)}t.topBoolOrEmptySchema=function(e){const{gen:t,schema:n,validateName:r}=e;!1===n?s(e,!1):"object"==typeof n&&!0===n.$async?t.return(i.default.data):(t.assign(o._`${r}.errors`,null),t.return(!0))},t.boolOrEmptySchema=function(e,t){const{gen:n,schema:r}=e;!1===r?(n.var(t,!1),s(e)):n.var(t,!0)}},7927:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.reportTypeError=t.checkDataTypes=t.checkDataType=t.coerceAndCheckDataType=t.getJSONTypes=t.getSchemaTypes=t.DataType=void 0;const r=n(3664),o=n(4566),i=n(1885),a=n(4475),s=n(6124);var l;function c(e){const t=Array.isArray(e)?e:e?[e]:[];if(t.every(r.isJSONType))return t;throw new Error("type must be JSONType or JSONType[]: "+t.join(","))}!function(e){e[e.Correct=0]="Correct",e[e.Wrong=1]="Wrong"}(l=t.DataType||(t.DataType={})),t.getSchemaTypes=function(e){const t=c(e.type);if(t.includes("null")){if(!1===e.nullable)throw new Error("type: null contradicts nullable: false")}else{if(!t.length&&void 0!==e.nullable)throw new Error('"nullable" cannot be used without "type"');!0===e.nullable&&t.push("null")}return t},t.getJSONTypes=c,t.coerceAndCheckDataType=function(e,t){const{gen:n,data:r,opts:i}=e,s=function(e,t){return t?e.filter((e=>u.has(e)||"array"===t&&"array"===e)):[]}(t,i.coerceTypes),c=t.length>0&&!(0===s.length&&1===t.length&&o.schemaHasRulesForType(e,t[0]));if(c){const o=d(t,r,i.strictNumbers,l.Wrong);n.if(o,(()=>{s.length?function(e,t,n){const{gen:r,data:o,opts:i}=e,s=r.let("dataType",a._`typeof ${o}`),l=r.let("coerced",a._`undefined`);"array"===i.coerceTypes&&r.if(a._`${s} == 'object' && Array.isArray(${o}) && ${o}.length == 1`,(()=>r.assign(o,a._`${o}[0]`).assign(s,a._`typeof ${o}`).if(d(t,o,i.strictNumbers),(()=>r.assign(l,o))))),r.if(a._`${l} !== undefined`);for(const e of n)(u.has(e)||"array"===e&&"array"===i.coerceTypes)&&c(e);function c(e){switch(e){case"string":return void r.elseIf(a._`${s} == "number" || ${s} == "boolean"`).assign(l,a._`"" + ${o}`).elseIf(a._`${o} === null`).assign(l,a._`""`);case"number":return void r.elseIf(a._`${s} == "boolean" || ${o} === null - || (${s} == "string" && ${o} && ${o} == +${o})`).assign(l,a._`+${o}`);case"integer":return void r.elseIf(a._`${s} === "boolean" || ${o} === null - || (${s} === "string" && ${o} && ${o} == +${o} && !(${o} % 1))`).assign(l,a._`+${o}`);case"boolean":return void r.elseIf(a._`${o} === "false" || ${o} === 0 || ${o} === null`).assign(l,!1).elseIf(a._`${o} === "true" || ${o} === 1`).assign(l,!0);case"null":return r.elseIf(a._`${o} === "" || ${o} === 0 || ${o} === false`),void r.assign(l,null);case"array":r.elseIf(a._`${s} === "string" || ${s} === "number" - || ${s} === "boolean" || ${o} === null`).assign(l,a._`[${o}]`)}}r.else(),h(e),r.endIf(),r.if(a._`${l} !== undefined`,(()=>{r.assign(o,l),function({gen:e,parentData:t,parentDataProperty:n},r){e.if(a._`${t} !== undefined`,(()=>e.assign(a._`${t}[${n}]`,r)))}(e,l)}))}(e,t,s):h(e)}))}return c};const u=new Set(["string","number","integer","boolean","null"]);function p(e,t,n,r=l.Correct){const o=r===l.Correct?a.operators.EQ:a.operators.NEQ;let i;switch(e){case"null":return a._`${t} ${o} null`;case"array":i=a._`Array.isArray(${t})`;break;case"object":i=a._`${t} && typeof ${t} == "object" && !Array.isArray(${t})`;break;case"integer":i=s(a._`!(${t} % 1) && !isNaN(${t})`);break;case"number":i=s();break;default:return a._`typeof ${t} ${o} ${e}`}return r===l.Correct?i:a.not(i);function s(e=a.nil){return a.and(a._`typeof ${t} == "number"`,e,n?a._`isFinite(${t})`:a.nil)}}function d(e,t,n,r){if(1===e.length)return p(e[0],t,n,r);let o;const i=s.toHash(e);if(i.array&&i.object){const e=a._`typeof ${t} != "object"`;o=i.null?e:a._`!${t} || ${e}`,delete i.null,delete i.array,delete i.object}else o=a.nil;i.number&&delete i.integer;for(const e in i)o=a.and(o,p(e,t,n,r));return o}t.checkDataType=p,t.checkDataTypes=d;const f={message:({schema:e})=>`must be ${e}`,params:({schema:e,schemaValue:t})=>"string"==typeof e?a._`{type: ${e}}`:a._`{type: ${t}}`};function h(e){const t=function(e){const{gen:t,data:n,schema:r}=e,o=s.schemaRefOrVal(e,r,"type");return{gen:t,keyword:"type",data:n,schema:r.type,schemaCode:o,schemaValue:o,parentSchema:r,params:{},it:e}}(e);i.reportError(t,f)}t.reportTypeError=h},2537:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.assignDefaults=void 0;const r=n(4475),o=n(6124);function i(e,t,n){const{gen:i,compositeRule:a,data:s,opts:l}=e;if(void 0===n)return;const c=r._`${s}${r.getProperty(t)}`;if(a)return void o.checkStrictMode(e,`default is ignored for: ${c}`);let u=r._`${c} === undefined`;"empty"===l.useDefaults&&(u=r._`${u} || ${c} === null || ${c} === ""`),i.if(u,r._`${c} = ${r.stringify(n)}`)}t.assignDefaults=function(e,t){const{properties:n,items:r}=e.schema;if("object"===t&&n)for(const t in n)i(e,t,n[t].default);else"array"===t&&Array.isArray(r)&&r.forEach(((t,n)=>i(e,n,t.default)))}},1321:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getData=t.KeywordCxt=t.validateFunctionCode=void 0;const r=n(7627),o=n(7927),i=n(4566),a=n(7927),s=n(2537),l=n(6488),c=n(4688),u=n(4475),p=n(5018),d=n(9826),f=n(6124),h=n(1885);function m({gen:e,validateName:t,schema:n,schemaEnv:r,opts:o},i){o.code.es5?e.func(t,u._`${p.default.data}, ${p.default.valCxt}`,r.$async,(()=>{e.code(u._`"use strict"; ${g(n,o)}`),function(e,t){e.if(p.default.valCxt,(()=>{e.var(p.default.instancePath,u._`${p.default.valCxt}.${p.default.instancePath}`),e.var(p.default.parentData,u._`${p.default.valCxt}.${p.default.parentData}`),e.var(p.default.parentDataProperty,u._`${p.default.valCxt}.${p.default.parentDataProperty}`),e.var(p.default.rootData,u._`${p.default.valCxt}.${p.default.rootData}`),t.dynamicRef&&e.var(p.default.dynamicAnchors,u._`${p.default.valCxt}.${p.default.dynamicAnchors}`)}),(()=>{e.var(p.default.instancePath,u._`""`),e.var(p.default.parentData,u._`undefined`),e.var(p.default.parentDataProperty,u._`undefined`),e.var(p.default.rootData,p.default.data),t.dynamicRef&&e.var(p.default.dynamicAnchors,u._`{}`)}))}(e,o),e.code(i)})):e.func(t,u._`${p.default.data}, ${function(e){return u._`{${p.default.instancePath}="", ${p.default.parentData}, ${p.default.parentDataProperty}, ${p.default.rootData}=${p.default.data}${e.dynamicRef?u._`, ${p.default.dynamicAnchors}={}`:u.nil}}={}`}(o)}`,r.$async,(()=>e.code(g(n,o)).code(i)))}function g(e,t){const n="object"==typeof e&&e[t.schemaId];return n&&(t.code.source||t.code.process)?u._`/*# sourceURL=${n} */`:u.nil}function y({schema:e,self:t}){if("boolean"==typeof e)return!e;for(const n in e)if(t.RULES.all[n])return!0;return!1}function v(e){return"boolean"!=typeof e.schema}function b(e){f.checkUnknownRules(e),function(e){const{schema:t,errSchemaPath:n,opts:r,self:o}=e;t.$ref&&r.ignoreKeywordsWithRef&&f.schemaHasRulesButRef(t,o.RULES)&&o.logger.warn(`$ref: keywords ignored in schema at path "${n}"`)}(e)}function w(e,t){if(e.opts.jtd)return k(e,[],!1,t);const n=o.getSchemaTypes(e.schema);k(e,n,!o.coerceAndCheckDataType(e,n),t)}function x({gen:e,schemaEnv:t,schema:n,errSchemaPath:r,opts:o}){const i=n.$comment;if(!0===o.$comment)e.code(u._`${p.default.self}.logger.log(${i})`);else if("function"==typeof o.$comment){const n=u.str`${r}/$comment`,o=e.scopeValue("root",{ref:t.root});e.code(u._`${p.default.self}.opts.$comment(${i}, ${n}, ${o}.schema)`)}}function k(e,t,n,r){const{gen:o,schema:s,data:l,allErrors:c,opts:d,self:h}=e,{RULES:m}=h;function g(f){i.shouldUseGroup(s,f)&&(f.type?(o.if(a.checkDataType(f.type,l,d.strictNumbers)),_(e,f),1===t.length&&t[0]===f.type&&n&&(o.else(),a.reportTypeError(e)),o.endIf()):_(e,f),c||o.if(u._`${p.default.errors} === ${r||0}`))}!s.$ref||!d.ignoreKeywordsWithRef&&f.schemaHasRulesButRef(s,m)?(d.jtd||function(e,t){!e.schemaEnv.meta&&e.opts.strictTypes&&(function(e,t){t.length&&(e.dataTypes.length?(t.forEach((t=>{O(e.dataTypes,t)||S(e,`type "${t}" not allowed by context "${e.dataTypes.join(",")}"`)})),e.dataTypes=e.dataTypes.filter((e=>O(t,e)))):e.dataTypes=t)}(e,t),e.opts.allowUnionTypes||function(e,t){t.length>1&&(2!==t.length||!t.includes("null"))&&S(e,"use allowUnionTypes to allow union type keyword")}(e,t),function(e,t){const n=e.self.RULES.all;for(const r in n){const o=n[r];if("object"==typeof o&&i.shouldUseRule(e.schema,o)){const{type:n}=o.definition;n.length&&!n.some((e=>{return r=e,(n=t).includes(r)||"number"===r&&n.includes("integer");var n,r}))&&S(e,`missing type "${n.join(",")}" for keyword "${r}"`)}}}(e,e.dataTypes))}(e,t),o.block((()=>{for(const e of m.rules)g(e);g(m.post)}))):o.block((()=>P(e,"$ref",m.all.$ref.definition)))}function _(e,t){const{gen:n,schema:r,opts:{useDefaults:o}}=e;o&&s.assignDefaults(e,t.type),n.block((()=>{for(const n of t.rules)i.shouldUseRule(r,n)&&P(e,n.keyword,n.definition,t.type)}))}function O(e,t){return e.includes(t)||"integer"===t&&e.includes("number")}function S(e,t){t+=` at "${e.schemaEnv.baseId+e.errSchemaPath}" (strictTypes)`,f.checkStrictMode(e,t,e.opts.strictTypes)}t.validateFunctionCode=function(e){v(e)&&(b(e),y(e))?function(e){const{schema:t,opts:n,gen:r}=e;m(e,(()=>{n.$comment&&t.$comment&&x(e),function(e){const{schema:t,opts:n}=e;void 0!==t.default&&n.useDefaults&&n.strictSchema&&f.checkStrictMode(e,"default is ignored in the schema root")}(e),r.let(p.default.vErrors,null),r.let(p.default.errors,0),n.unevaluated&&function(e){const{gen:t,validateName:n}=e;e.evaluated=t.const("evaluated",u._`${n}.evaluated`),t.if(u._`${e.evaluated}.dynamicProps`,(()=>t.assign(u._`${e.evaluated}.props`,u._`undefined`))),t.if(u._`${e.evaluated}.dynamicItems`,(()=>t.assign(u._`${e.evaluated}.items`,u._`undefined`)))}(e),w(e),function(e){const{gen:t,schemaEnv:n,validateName:r,ValidationError:o,opts:i}=e;n.$async?t.if(u._`${p.default.errors} === 0`,(()=>t.return(p.default.data)),(()=>t.throw(u._`new ${o}(${p.default.vErrors})`))):(t.assign(u._`${r}.errors`,p.default.vErrors),i.unevaluated&&function({gen:e,evaluated:t,props:n,items:r}){n instanceof u.Name&&e.assign(u._`${t}.props`,n),r instanceof u.Name&&e.assign(u._`${t}.items`,r)}(e),t.return(u._`${p.default.errors} === 0`))}(e)}))}(e):m(e,(()=>r.topBoolOrEmptySchema(e)))};class E{constructor(e,t,n){if(l.validateKeywordUsage(e,t,n),this.gen=e.gen,this.allErrors=e.allErrors,this.keyword=n,this.data=e.data,this.schema=e.schema[n],this.$data=t.$data&&e.opts.$data&&this.schema&&this.schema.$data,this.schemaValue=f.schemaRefOrVal(e,this.schema,n,this.$data),this.schemaType=t.schemaType,this.parentSchema=e.schema,this.params={},this.it=e,this.def=t,this.$data)this.schemaCode=e.gen.const("vSchema",C(this.$data,e));else if(this.schemaCode=this.schemaValue,!l.validSchemaType(this.schema,t.schemaType,t.allowUndefined))throw new Error(`${n} value must be ${JSON.stringify(t.schemaType)}`);("code"in t?t.trackErrors:!1!==t.errors)&&(this.errsCount=e.gen.const("_errs",p.default.errors))}result(e,t,n){this.gen.if(u.not(e)),n?n():this.error(),t?(this.gen.else(),t(),this.allErrors&&this.gen.endIf()):this.allErrors?this.gen.endIf():this.gen.else()}pass(e,t){this.result(e,void 0,t)}fail(e){if(void 0===e)return this.error(),void(this.allErrors||this.gen.if(!1));this.gen.if(e),this.error(),this.allErrors?this.gen.endIf():this.gen.else()}fail$data(e){if(!this.$data)return this.fail(e);const{schemaCode:t}=this;this.fail(u._`${t} !== undefined && (${u.or(this.invalid$data(),e)})`)}error(e,t,n){if(t)return this.setParams(t),this._error(e,n),void this.setParams({});this._error(e,n)}_error(e,t){(e?h.reportExtraError:h.reportError)(this,this.def.error,t)}$dataError(){h.reportError(this,this.def.$dataError||h.keyword$DataError)}reset(){if(void 0===this.errsCount)throw new Error('add "trackErrors" to keyword definition');h.resetErrorsCount(this.gen,this.errsCount)}ok(e){this.allErrors||this.gen.if(e)}setParams(e,t){t?Object.assign(this.params,e):this.params=e}block$data(e,t,n=u.nil){this.gen.block((()=>{this.check$data(e,n),t()}))}check$data(e=u.nil,t=u.nil){if(!this.$data)return;const{gen:n,schemaCode:r,schemaType:o,def:i}=this;n.if(u.or(u._`${r} === undefined`,t)),e!==u.nil&&n.assign(e,!0),(o.length||i.validateSchema)&&(n.elseIf(this.invalid$data()),this.$dataError(),e!==u.nil&&n.assign(e,!1)),n.else()}invalid$data(){const{gen:e,schemaCode:t,schemaType:n,def:r,it:o}=this;return u.or(function(){if(n.length){if(!(t instanceof u.Name))throw new Error("ajv implementation error");const e=Array.isArray(n)?n:[n];return u._`${a.checkDataTypes(e,t,o.opts.strictNumbers,a.DataType.Wrong)}`}return u.nil}(),function(){if(r.validateSchema){const n=e.scopeValue("validate$data",{ref:r.validateSchema});return u._`!${n}(${t})`}return u.nil}())}subschema(e,t){const n=c.getSubschema(this.it,e);c.extendSubschemaData(n,this.it,e),c.extendSubschemaMode(n,e);const o={...this.it,...n,items:void 0,props:void 0};return function(e,t){v(e)&&(b(e),y(e))?function(e,t){const{schema:n,gen:r,opts:o}=e;o.$comment&&n.$comment&&x(e),function(e){const t=e.schema[e.opts.schemaId];t&&(e.baseId=d.resolveUrl(e.baseId,t))}(e),function(e){if(e.schema.$async&&!e.schemaEnv.$async)throw new Error("async schema in sync schema")}(e);const i=r.const("_errs",p.default.errors);w(e,i),r.var(t,u._`${i} === ${p.default.errors}`)}(e,t):r.boolOrEmptySchema(e,t)}(o,t),o}mergeEvaluated(e,t){const{it:n,gen:r}=this;n.opts.unevaluated&&(!0!==n.props&&void 0!==e.props&&(n.props=f.mergeEvaluated.props(r,e.props,n.props,t)),!0!==n.items&&void 0!==e.items&&(n.items=f.mergeEvaluated.items(r,e.items,n.items,t)))}mergeValidEvaluated(e,t){const{it:n,gen:r}=this;if(n.opts.unevaluated&&(!0!==n.props||!0!==n.items))return r.if(t,(()=>this.mergeEvaluated(e,u.Name))),!0}}function P(e,t,n,r){const o=new E(e,n,t);"code"in n?n.code(o,r):o.$data&&n.validate?l.funcKeywordCode(o,n):"macro"in n?l.macroKeywordCode(o,n):(n.compile||n.validate)&&l.funcKeywordCode(o,n)}t.KeywordCxt=E;const A=/^\/(?:[^~]|~0|~1)*$/,$=/^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;function C(e,{dataLevel:t,dataNames:n,dataPathArr:r}){let o,i;if(""===e)return p.default.rootData;if("/"===e[0]){if(!A.test(e))throw new Error(`Invalid JSON-pointer: ${e}`);o=e,i=p.default.rootData}else{const a=$.exec(e);if(!a)throw new Error(`Invalid JSON-pointer: ${e}`);const s=+a[1];if(o=a[2],"#"===o){if(s>=t)throw new Error(l("property/index",s));return r[t-s]}if(s>t)throw new Error(l("data",s));if(i=n[t-s],!o)return i}let a=i;const s=o.split("/");for(const e of s)e&&(i=u._`${i}${u.getProperty(f.unescapeJsonPointer(e))}`,a=u._`${a} && ${i}`);return a;function l(e,n){return`Cannot access ${e} ${n} levels up, current level is ${t}`}}t.getData=C},6488:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateKeywordUsage=t.validSchemaType=t.funcKeywordCode=t.macroKeywordCode=void 0;const r=n(4475),o=n(5018),i=n(8619),a=n(1885);function s(e){const{gen:t,data:n,it:o}=e;t.if(o.parentData,(()=>t.assign(n,r._`${o.parentData}[${o.parentDataProperty}]`)))}function l(e,t,n){if(void 0===n)throw new Error(`keyword "${t}" failed to compile`);return e.scopeValue("keyword","function"==typeof n?{ref:n}:{ref:n,code:r.stringify(n)})}t.macroKeywordCode=function(e,t){const{gen:n,keyword:o,schema:i,parentSchema:a,it:s}=e,c=t.macro.call(s.self,i,a,s),u=l(n,o,c);!1!==s.opts.validateSchema&&s.self.validateSchema(c,!0);const p=n.name("valid");e.subschema({schema:c,schemaPath:r.nil,errSchemaPath:`${s.errSchemaPath}/${o}`,topSchemaRef:u,compositeRule:!0},p),e.pass(p,(()=>e.error(!0)))},t.funcKeywordCode=function(e,t){var n;const{gen:c,keyword:u,schema:p,parentSchema:d,$data:f,it:h}=e;!function({schemaEnv:e},t){if(t.async&&!e.$async)throw new Error("async keyword in sync schema")}(h,t);const m=!f&&t.compile?t.compile.call(h.self,p,d,h):t.validate,g=l(c,u,m),y=c.let("valid");function v(n=(t.async?r._`await `:r.nil)){const a=h.opts.passContext?o.default.this:o.default.self,s=!("compile"in t&&!f||!1===t.schema);c.assign(y,r._`${n}${i.callValidateCode(e,g,a,s)}`,t.modifying)}function b(e){var n;c.if(r.not(null!==(n=t.valid)&&void 0!==n?n:y),e)}e.block$data(y,(function(){if(!1===t.errors)v(),t.modifying&&s(e),b((()=>e.error()));else{const n=t.async?function(){const e=c.let("ruleErrs",null);return c.try((()=>v(r._`await `)),(t=>c.assign(y,!1).if(r._`${t} instanceof ${h.ValidationError}`,(()=>c.assign(e,r._`${t}.errors`)),(()=>c.throw(t))))),e}():function(){const e=r._`${g}.errors`;return c.assign(e,null),v(r.nil),e}();t.modifying&&s(e),b((()=>function(e,t){const{gen:n}=e;n.if(r._`Array.isArray(${t})`,(()=>{n.assign(o.default.vErrors,r._`${o.default.vErrors} === null ? ${t} : ${o.default.vErrors}.concat(${t})`).assign(o.default.errors,r._`${o.default.vErrors}.length`),a.extendErrors(e)}),(()=>e.error()))}(e,n)))}})),e.ok(null!==(n=t.valid)&&void 0!==n?n:y)},t.validSchemaType=function(e,t,n=!1){return!t.length||t.some((t=>"array"===t?Array.isArray(e):"object"===t?e&&"object"==typeof e&&!Array.isArray(e):typeof e==t||n&&void 0===e))},t.validateKeywordUsage=function({schema:e,opts:t,self:n,errSchemaPath:r},o,i){if(Array.isArray(o.keyword)?!o.keyword.includes(i):o.keyword!==i)throw new Error("ajv implementation error");const a=o.dependencies;if(null==a?void 0:a.some((t=>!Object.prototype.hasOwnProperty.call(e,t))))throw new Error(`parent schema must have dependencies of ${i}: ${a.join(",")}`);if(o.validateSchema&&!o.validateSchema(e[i])){const e=`keyword "${i}" value is invalid at path "${r}": `+n.errorsText(o.validateSchema.errors);if("log"!==t.validateSchema)throw new Error(e);n.logger.error(e)}}},4688:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.extendSubschemaMode=t.extendSubschemaData=t.getSubschema=void 0;const r=n(4475),o=n(6124);t.getSubschema=function(e,{keyword:t,schemaProp:n,schema:i,schemaPath:a,errSchemaPath:s,topSchemaRef:l}){if(void 0!==t&&void 0!==i)throw new Error('both "keyword" and "schema" passed, only one allowed');if(void 0!==t){const i=e.schema[t];return void 0===n?{schema:i,schemaPath:r._`${e.schemaPath}${r.getProperty(t)}`,errSchemaPath:`${e.errSchemaPath}/${t}`}:{schema:i[n],schemaPath:r._`${e.schemaPath}${r.getProperty(t)}${r.getProperty(n)}`,errSchemaPath:`${e.errSchemaPath}/${t}/${o.escapeFragment(n)}`}}if(void 0!==i){if(void 0===a||void 0===s||void 0===l)throw new Error('"schemaPath", "errSchemaPath" and "topSchemaRef" are required with "schema"');return{schema:i,schemaPath:a,topSchemaRef:l,errSchemaPath:s}}throw new Error('either "keyword" or "schema" must be passed')},t.extendSubschemaData=function(e,t,{dataProp:n,dataPropType:i,data:a,dataTypes:s,propertyName:l}){if(void 0!==a&&void 0!==n)throw new Error('both "data" and "dataProp" passed, only one allowed');const{gen:c}=t;if(void 0!==n){const{errorPath:a,dataPathArr:s,opts:l}=t;u(c.let("data",r._`${t.data}${r.getProperty(n)}`,!0)),e.errorPath=r.str`${a}${o.getErrorPath(n,i,l.jsPropertySyntax)}`,e.parentDataProperty=r._`${n}`,e.dataPathArr=[...s,e.parentDataProperty]}function u(n){e.data=n,e.dataLevel=t.dataLevel+1,e.dataTypes=[],t.definedProperties=new Set,e.parentData=t.data,e.dataNames=[...t.dataNames,n]}void 0!==a&&(u(a instanceof r.Name?a:c.let("data",a,!0)),void 0!==l&&(e.propertyName=l)),s&&(e.dataTypes=s)},t.extendSubschemaMode=function(e,{jtdDiscriminator:t,jtdMetadata:n,compositeRule:r,createErrors:o,allErrors:i}){void 0!==r&&(e.compositeRule=r),void 0!==o&&(e.createErrors=o),void 0!==i&&(e.allErrors=i),e.jtdDiscriminator=t,e.jtdMetadata=n}},3325:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CodeGen=t.Name=t.nil=t.stringify=t.str=t._=t.KeywordCxt=void 0;var r=n(1321);Object.defineProperty(t,"KeywordCxt",{enumerable:!0,get:function(){return r.KeywordCxt}});var o=n(4475);Object.defineProperty(t,"_",{enumerable:!0,get:function(){return o._}}),Object.defineProperty(t,"str",{enumerable:!0,get:function(){return o.str}}),Object.defineProperty(t,"stringify",{enumerable:!0,get:function(){return o.stringify}}),Object.defineProperty(t,"nil",{enumerable:!0,get:function(){return o.nil}}),Object.defineProperty(t,"Name",{enumerable:!0,get:function(){return o.Name}}),Object.defineProperty(t,"CodeGen",{enumerable:!0,get:function(){return o.CodeGen}});const i=n(8451),a=n(4143),s=n(3664),l=n(7805),c=n(4475),u=n(9826),p=n(7927),d=n(6124),f=n(425),h=["removeAdditional","useDefaults","coerceTypes"],m=new Set(["validate","serialize","parse","wrapper","root","schema","keyword","pattern","formats","validate$data","func","obj","Error"]),g={errorDataPath:"",format:"`validateFormats: false` can be used instead.",nullable:'"nullable" keyword is supported by default.',jsonPointers:"Deprecated jsPropertySyntax can be used instead.",extendRefs:"Deprecated ignoreKeywordsWithRef can be used instead.",missingRefs:"Pass empty schema with $id that should be ignored to ajv.addSchema.",processCode:"Use option `code: {process: (code, schemaEnv: object) => string}`",sourceCode:"Use option `code: {source: true}`",strictDefaults:"It is default now, see option `strict`.",strictKeywords:"It is default now, see option `strict`.",uniqueItems:'"uniqueItems" keyword is always validated.',unknownFormats:"Disable strict mode or pass `true` to `ajv.addFormat` (or `formats` option).",cache:"Map is used as cache, schema object as key.",serialize:"Map is used as cache, schema object as key.",ajvErrors:"It is default now."},y={ignoreKeywordsWithRef:"",jsPropertySyntax:"",unicode:'"minLength"/"maxLength" account for unicode characters by default.'};function v(e){var t,n,r,o,i,a,s,l,c,u,p,d,f,h,m,g,y,v,b,w,x,k;const _=e.strict,O=null===(t=e.code)||void 0===t?void 0:t.optimize,S=!0===O||void 0===O?1:O||0;return{strictSchema:null===(r=null!==(n=e.strictSchema)&&void 0!==n?n:_)||void 0===r||r,strictNumbers:null===(i=null!==(o=e.strictNumbers)&&void 0!==o?o:_)||void 0===i||i,strictTypes:null!==(s=null!==(a=e.strictTypes)&&void 0!==a?a:_)&&void 0!==s?s:"log",strictTuples:null!==(c=null!==(l=e.strictTuples)&&void 0!==l?l:_)&&void 0!==c?c:"log",strictRequired:null!==(p=null!==(u=e.strictRequired)&&void 0!==u?u:_)&&void 0!==p&&p,code:e.code?{...e.code,optimize:S}:{optimize:S},loopRequired:null!==(d=e.loopRequired)&&void 0!==d?d:200,loopEnum:null!==(f=e.loopEnum)&&void 0!==f?f:200,meta:null===(h=e.meta)||void 0===h||h,messages:null===(m=e.messages)||void 0===m||m,inlineRefs:null===(g=e.inlineRefs)||void 0===g||g,schemaId:null!==(y=e.schemaId)&&void 0!==y?y:"$id",addUsedSchema:null===(v=e.addUsedSchema)||void 0===v||v,validateSchema:null===(b=e.validateSchema)||void 0===b||b,validateFormats:null===(w=e.validateFormats)||void 0===w||w,unicodeRegExp:null===(x=e.unicodeRegExp)||void 0===x||x,int32range:null===(k=e.int32range)||void 0===k||k}}class b{constructor(e={}){this.schemas={},this.refs={},this.formats={},this._compilations=new Set,this._loading={},this._cache=new Map,e=this.opts={...e,...v(e)};const{es5:t,lines:n}=this.opts.code;this.scope=new c.ValueScope({scope:{},prefixes:m,es5:t,lines:n}),this.logger=function(e){if(!1===e)return E;if(void 0===e)return console;if(e.log&&e.warn&&e.error)return e;throw new Error("logger must implement log, warn and error methods")}(e.logger);const r=e.validateFormats;e.validateFormats=!1,this.RULES=s.getRules(),w.call(this,g,e,"NOT SUPPORTED"),w.call(this,y,e,"DEPRECATED","warn"),this._metaOpts=S.call(this),e.formats&&_.call(this),this._addVocabularies(),this._addDefaultMetaSchema(),e.keywords&&O.call(this,e.keywords),"object"==typeof e.meta&&this.addMetaSchema(e.meta),k.call(this),e.validateFormats=r}_addVocabularies(){this.addKeyword("$async")}_addDefaultMetaSchema(){const{$data:e,meta:t,schemaId:n}=this.opts;let r=f;"id"===n&&(r={...f},r.id=r.$id,delete r.$id),t&&e&&this.addMetaSchema(r,r[n],!1)}defaultMeta(){const{meta:e,schemaId:t}=this.opts;return this.opts.defaultMeta="object"==typeof e?e[t]||e:void 0}validate(e,t){let n;if("string"==typeof e){if(n=this.getSchema(e),!n)throw new Error(`no schema with key or ref "${e}"`)}else n=this.compile(e);const r=n(t);return"$async"in n||(this.errors=n.errors),r}compile(e,t){const n=this._addSchema(e,t);return n.validate||this._compileSchemaEnv(n)}compileAsync(e,t){if("function"!=typeof this.opts.loadSchema)throw new Error("options.loadSchema should be a function");const{loadSchema:n}=this.opts;return r.call(this,e,t);async function r(e,t){await o.call(this,e.$schema);const n=this._addSchema(e,t);return n.validate||i.call(this,n)}async function o(e){e&&!this.getSchema(e)&&await r.call(this,{$ref:e},!0)}async function i(e){try{return this._compileSchemaEnv(e)}catch(t){if(!(t instanceof a.default))throw t;return s.call(this,t),await l.call(this,t.missingSchema),i.call(this,e)}}function s({missingSchema:e,missingRef:t}){if(this.refs[e])throw new Error(`AnySchema ${e} is loaded but ${t} cannot be resolved`)}async function l(e){const n=await c.call(this,e);this.refs[e]||await o.call(this,n.$schema),this.refs[e]||this.addSchema(n,e,t)}async function c(e){const t=this._loading[e];if(t)return t;try{return await(this._loading[e]=n(e))}finally{delete this._loading[e]}}}addSchema(e,t,n,r=this.opts.validateSchema){if(Array.isArray(e)){for(const t of e)this.addSchema(t,void 0,n,r);return this}let o;if("object"==typeof e){const{schemaId:t}=this.opts;if(o=e[t],void 0!==o&&"string"!=typeof o)throw new Error(`schema ${t} must be string`)}return t=u.normalizeId(t||o),this._checkUnique(t),this.schemas[t]=this._addSchema(e,n,t,r,!0),this}addMetaSchema(e,t,n=this.opts.validateSchema){return this.addSchema(e,t,!0,n),this}validateSchema(e,t){if("boolean"==typeof e)return!0;let n;if(n=e.$schema,void 0!==n&&"string"!=typeof n)throw new Error("$schema must be a string");if(n=n||this.opts.defaultMeta||this.defaultMeta(),!n)return this.logger.warn("meta-schema not available"),this.errors=null,!0;const r=this.validate(n,e);if(!r&&t){const e="schema is invalid: "+this.errorsText();if("log"!==this.opts.validateSchema)throw new Error(e);this.logger.error(e)}return r}getSchema(e){let t;for(;"string"==typeof(t=x.call(this,e));)e=t;if(void 0===t){const{schemaId:n}=this.opts,r=new l.SchemaEnv({schema:{},schemaId:n});if(t=l.resolveSchema.call(this,r,e),!t)return;this.refs[e]=t}return t.validate||this._compileSchemaEnv(t)}removeSchema(e){if(e instanceof RegExp)return this._removeAllSchemas(this.schemas,e),this._removeAllSchemas(this.refs,e),this;switch(typeof e){case"undefined":return this._removeAllSchemas(this.schemas),this._removeAllSchemas(this.refs),this._cache.clear(),this;case"string":{const t=x.call(this,e);return"object"==typeof t&&this._cache.delete(t.schema),delete this.schemas[e],delete this.refs[e],this}case"object":{const t=e;this._cache.delete(t);let n=e[this.opts.schemaId];return n&&(n=u.normalizeId(n),delete this.schemas[n],delete this.refs[n]),this}default:throw new Error("ajv.removeSchema: invalid parameter")}}addVocabulary(e){for(const t of e)this.addKeyword(t);return this}addKeyword(e,t){let n;if("string"==typeof e)n=e,"object"==typeof t&&(this.logger.warn("these parameters are deprecated, see docs for addKeyword"),t.keyword=n);else{if("object"!=typeof e||void 0!==t)throw new Error("invalid addKeywords parameters");if(n=(t=e).keyword,Array.isArray(n)&&!n.length)throw new Error("addKeywords: keyword must be string or non-empty array")}if(A.call(this,n,t),!t)return d.eachItem(n,(e=>$.call(this,e))),this;R.call(this,t);const r={...t,type:p.getJSONTypes(t.type),schemaType:p.getJSONTypes(t.schemaType)};return d.eachItem(n,0===r.type.length?e=>$.call(this,e,r):e=>r.type.forEach((t=>$.call(this,e,r,t)))),this}getKeyword(e){const t=this.RULES.all[e];return"object"==typeof t?t.definition:!!t}removeKeyword(e){const{RULES:t}=this;delete t.keywords[e],delete t.all[e];for(const n of t.rules){const t=n.rules.findIndex((t=>t.keyword===e));t>=0&&n.rules.splice(t,1)}return this}addFormat(e,t){return"string"==typeof t&&(t=new RegExp(t)),this.formats[e]=t,this}errorsText(e=this.errors,{separator:t=", ",dataVar:n="data"}={}){return e&&0!==e.length?e.map((e=>`${n}${e.instancePath} ${e.message}`)).reduce(((e,n)=>e+t+n)):"No errors"}$dataMetaSchema(e,t){const n=this.RULES.all;e=JSON.parse(JSON.stringify(e));for(const r of t){const t=r.split("/").slice(1);let o=e;for(const e of t)o=o[e];for(const e in n){const t=n[e];if("object"!=typeof t)continue;const{$data:r}=t.definition,i=o[e];r&&i&&(o[e]=T(i))}}return e}_removeAllSchemas(e,t){for(const n in e){const r=e[n];t&&!t.test(n)||("string"==typeof r?delete e[n]:r&&!r.meta&&(this._cache.delete(r.schema),delete e[n]))}}_addSchema(e,t,n,r=this.opts.validateSchema,o=this.opts.addUsedSchema){let i;const{schemaId:a}=this.opts;if("object"==typeof e)i=e[a];else{if(this.opts.jtd)throw new Error("schema must be object");if("boolean"!=typeof e)throw new Error("schema must be object or boolean")}let s=this._cache.get(e);if(void 0!==s)return s;const c=u.getSchemaRefs.call(this,e);return n=u.normalizeId(i||n),s=new l.SchemaEnv({schema:e,schemaId:a,meta:t,baseId:n,localRefs:c}),this._cache.set(s.schema,s),o&&!n.startsWith("#")&&(n&&this._checkUnique(n),this.refs[n]=s),r&&this.validateSchema(e,!0),s}_checkUnique(e){if(this.schemas[e]||this.refs[e])throw new Error(`schema with key or id "${e}" already exists`)}_compileSchemaEnv(e){if(e.meta?this._compileMetaSchema(e):l.compileSchema.call(this,e),!e.validate)throw new Error("ajv implementation error");return e.validate}_compileMetaSchema(e){const t=this.opts;this.opts=this._metaOpts;try{l.compileSchema.call(this,e)}finally{this.opts=t}}}function w(e,t,n,r="error"){for(const o in e){const i=o;i in t&&this.logger[r](`${n}: option ${o}. ${e[i]}`)}}function x(e){return e=u.normalizeId(e),this.schemas[e]||this.refs[e]}function k(){const e=this.opts.schemas;if(e)if(Array.isArray(e))this.addSchema(e);else for(const t in e)this.addSchema(e[t],t)}function _(){for(const e in this.opts.formats){const t=this.opts.formats[e];t&&this.addFormat(e,t)}}function O(e){if(Array.isArray(e))this.addVocabulary(e);else{this.logger.warn("keywords option as map is deprecated, pass array");for(const t in e){const n=e[t];n.keyword||(n.keyword=t),this.addKeyword(n)}}}function S(){const e={...this.opts};for(const t of h)delete e[t];return e}t.default=b,b.ValidationError=i.default,b.MissingRefError=a.default;const E={log(){},warn(){},error(){}},P=/^[a-z_$][a-z0-9_$:-]*$/i;function A(e,t){const{RULES:n}=this;if(d.eachItem(e,(e=>{if(n.keywords[e])throw new Error(`Keyword ${e} is already defined`);if(!P.test(e))throw new Error(`Keyword ${e} has invalid name`)})),t&&t.$data&&!("code"in t)&&!("validate"in t))throw new Error('$data keyword must have "code" or "validate" function')}function $(e,t,n){var r;const o=null==t?void 0:t.post;if(n&&o)throw new Error('keyword with "post" flag cannot have "type"');const{RULES:i}=this;let a=o?i.post:i.rules.find((({type:e})=>e===n));if(a||(a={type:n,rules:[]},i.rules.push(a)),i.keywords[e]=!0,!t)return;const s={keyword:e,definition:{...t,type:p.getJSONTypes(t.type),schemaType:p.getJSONTypes(t.schemaType)}};t.before?C.call(this,a,s,t.before):a.rules.push(s),i.all[e]=s,null===(r=t.implements)||void 0===r||r.forEach((e=>this.addKeyword(e)))}function C(e,t,n){const r=e.rules.findIndex((e=>e.keyword===n));r>=0?e.rules.splice(r,0,t):(e.rules.push(t),this.logger.warn(`rule ${n} is not defined`))}function R(e){let{metaSchema:t}=e;void 0!==t&&(e.$data&&this.opts.$data&&(t=T(t)),e.validateSchema=this.compile(t,!0))}const j={$ref:"https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#"};function T(e){return{anyOf:[e,j]}}},412:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4063);r.code='require("ajv/dist/runtime/equal").default',t.default=r},5872:function(e,t){"use strict";function n(e){const t=e.length;let n,r=0,o=0;for(;o=55296&&n<=56319&&or.str`must NOT have more than ${e} items`,params:({params:{len:e}})=>r._`{limit: ${e}}`},code(e){const{parentSchema:t,it:n}=e,{items:r}=t;Array.isArray(r)?a(e,r):o.checkStrictMode(n,'"additionalItems" is ignored when "items" is not an array of schemas')}};function a(e,t){const{gen:n,schema:i,data:a,keyword:s,it:l}=e;l.items=!0;const c=n.const("len",r._`${a}.length`);if(!1===i)e.setParams({len:t.length}),e.pass(r._`${c} <= ${t.length}`);else if("object"==typeof i&&!o.alwaysValidSchema(l,i)){const i=n.var("valid",r._`${c} <= ${t.length}`);n.if(r.not(i),(()=>function(i){n.forRange("i",t.length,c,(t=>{e.subschema({keyword:s,dataProp:t,dataPropType:o.Type.Num},i),l.allErrors||n.if(r.not(i),(()=>n.break()))}))}(i))),e.ok(i)}}t.validateAdditionalItems=a,t.default=i},1422:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(8619),o=n(4475),i=n(5018),a=n(6124),s={keyword:"additionalProperties",type:["object"],schemaType:["boolean","object"],allowUndefined:!0,trackErrors:!0,error:{message:"must NOT have additional properties",params:({params:e})=>o._`{additionalProperty: ${e.additionalProperty}}`},code(e){const{gen:t,parentSchema:n,data:s,errsCount:l,it:c}=e,{schema:u=c.opts.defaultAdditionalProperties}=e;if(!l)throw new Error("ajv implementation error");const{allErrors:p,opts:d}=c;if(c.props=!0,"all"!==d.removeAdditional&&a.alwaysValidSchema(c,u))return;const f=r.allSchemaProperties(n.properties),h=r.allSchemaProperties(n.patternProperties);function m(e){t.code(o._`delete ${s}[${e}]`)}function g(n){if("all"===d.removeAdditional||d.removeAdditional&&!1===u)m(n);else{if(!1===u)return e.setParams({additionalProperty:n}),e.error(),void(p||t.break());if("object"==typeof u&&!a.alwaysValidSchema(c,u)){const r=t.name("valid");"failing"===d.removeAdditional?(y(n,r,!1),t.if(o.not(r),(()=>{e.reset(),m(n)}))):(y(n,r),p||t.if(o.not(r),(()=>t.break())))}}}function y(t,n,r){const o={keyword:"additionalProperties",dataProp:t,dataPropType:a.Type.Str};!1===r&&Object.assign(o,{compositeRule:!0,createErrors:!1,allErrors:!1}),e.subschema(o,n)}t.forIn("key",s,(i=>{f.length||h.length?t.if(function(i){let s;if(f.length>8){const e=a.schemaRefOrVal(c,n.properties,"properties");s=r.isOwnProperty(t,e,i)}else s=f.length?o.or(...f.map((e=>o._`${i} === ${e}`))):o.nil;return h.length&&(s=o.or(s,...h.map((t=>o._`${r.usePattern(e,t)}.test(${i})`)))),o.not(s)}(i),(()=>g(i))):g(i)})),e.ok(o._`${l} === ${i.default.errors}`)}};t.default=s},5716:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(6124),o={keyword:"allOf",schemaType:"array",code(e){const{gen:t,schema:n,it:o}=e;if(!Array.isArray(n))throw new Error("ajv implementation error");const i=t.name("valid");n.forEach(((t,n)=>{if(r.alwaysValidSchema(o,t))return;const a=e.subschema({keyword:"allOf",schemaProp:n},i);e.ok(i),e.mergeEvaluated(a)}))}};t.default=o},1668:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r={keyword:"anyOf",schemaType:"array",trackErrors:!0,code:n(8619).validateUnion,error:{message:"must match a schema in anyOf"}};t.default=r},9564:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4475),o=n(6124),i={keyword:"contains",type:"array",schemaType:["object","boolean"],before:"uniqueItems",trackErrors:!0,error:{message:({params:{min:e,max:t}})=>void 0===t?r.str`must contain at least ${e} valid item(s)`:r.str`must contain at least ${e} and no more than ${t} valid item(s)`,params:({params:{min:e,max:t}})=>void 0===t?r._`{minContains: ${e}}`:r._`{minContains: ${e}, maxContains: ${t}}`},code(e){const{gen:t,schema:n,parentSchema:i,data:a,it:s}=e;let l,c;const{minContains:u,maxContains:p}=i;s.opts.next?(l=void 0===u?1:u,c=p):l=1;const d=t.const("len",r._`${a}.length`);if(e.setParams({min:l,max:c}),void 0===c&&0===l)return void o.checkStrictMode(s,'"minContains" == 0 without "maxContains": "contains" keyword ignored');if(void 0!==c&&l>c)return o.checkStrictMode(s,'"minContains" > "maxContains" is always invalid'),void e.fail();if(o.alwaysValidSchema(s,n)){let t=r._`${d} >= ${l}`;return void 0!==c&&(t=r._`${t} && ${d} <= ${c}`),void e.pass(t)}s.items=!0;const f=t.name("valid");if(void 0===c&&1===l)h(f,(()=>t.if(f,(()=>t.break()))));else{t.let(f,!1);const e=t.name("_valid"),n=t.let("count",0);h(e,(()=>t.if(e,(()=>function(e){t.code(r._`${e}++`),void 0===c?t.if(r._`${e} >= ${l}`,(()=>t.assign(f,!0).break())):(t.if(r._`${e} > ${c}`,(()=>t.assign(f,!1).break())),1===l?t.assign(f,!0):t.if(r._`${e} >= ${l}`,(()=>t.assign(f,!0))))}(n)))))}function h(n,r){t.forRange("i",0,d,(t=>{e.subschema({keyword:"contains",dataProp:t,dataPropType:o.Type.Num,compositeRule:!0},n),r()}))}e.result(f,(()=>e.reset()))}};t.default=i},1117:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateSchemaDeps=t.validatePropertyDeps=t.error=void 0;const r=n(4475),o=n(6124),i=n(8619);t.error={message:({params:{property:e,depsCount:t,deps:n}})=>{const o=1===t?"property":"properties";return r.str`must have ${o} ${n} when property ${e} is present`},params:({params:{property:e,depsCount:t,deps:n,missingProperty:o}})=>r._`{property: ${e}, - missingProperty: ${o}, - depsCount: ${t}, - deps: ${n}}`};const a={keyword:"dependencies",type:"object",schemaType:"object",error:t.error,code(e){const[t,n]=function({schema:e}){const t={},n={};for(const r in e)"__proto__"!==r&&((Array.isArray(e[r])?t:n)[r]=e[r]);return[t,n]}(e);s(e,t),l(e,n)}};function s(e,t=e.schema){const{gen:n,data:o,it:a}=e;if(0===Object.keys(t).length)return;const s=n.let("missing");for(const l in t){const c=t[l];if(0===c.length)continue;const u=i.propertyInData(n,o,l,a.opts.ownProperties);e.setParams({property:l,depsCount:c.length,deps:c.join(", ")}),a.allErrors?n.if(u,(()=>{for(const t of c)i.checkReportMissingProp(e,t)})):(n.if(r._`${u} && (${i.checkMissingProp(e,c,s)})`),i.reportMissingProp(e,s),n.else())}}function l(e,t=e.schema){const{gen:n,data:r,keyword:a,it:s}=e,l=n.name("valid");for(const c in t)o.alwaysValidSchema(s,t[c])||(n.if(i.propertyInData(n,r,c,s.opts.ownProperties),(()=>{const t=e.subschema({keyword:a,schemaProp:c},l);e.mergeValidEvaluated(t,l)}),(()=>n.var(l,!0))),e.ok(l))}t.validatePropertyDeps=s,t.validateSchemaDeps=l,t.default=a},5184:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4475),o=n(6124),i={keyword:"if",schemaType:["object","boolean"],trackErrors:!0,error:{message:({params:e})=>r.str`must match "${e.ifClause}" schema`,params:({params:e})=>r._`{failingKeyword: ${e.ifClause}}`},code(e){const{gen:t,parentSchema:n,it:i}=e;void 0===n.then&&void 0===n.else&&o.checkStrictMode(i,'"if" without "then" and "else" is ignored');const s=a(i,"then"),l=a(i,"else");if(!s&&!l)return;const c=t.let("valid",!0),u=t.name("_valid");if(function(){const t=e.subschema({keyword:"if",compositeRule:!0,createErrors:!1,allErrors:!1},u);e.mergeEvaluated(t)}(),e.reset(),s&&l){const n=t.let("ifClause");e.setParams({ifClause:n}),t.if(u,p("then",n),p("else",n))}else s?t.if(u,p("then")):t.if(r.not(u),p("else"));function p(n,o){return()=>{const i=e.subschema({keyword:n},u);t.assign(c,u),e.mergeValidEvaluated(i,c),o?t.assign(o,r._`${n}`):e.setParams({ifClause:n})}}e.pass(c,(()=>e.error(!0)))}};function a(e,t){const n=e.schema[t];return void 0!==n&&!o.alwaysValidSchema(e,n)}t.default=i},9616:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(3074),o=n(6988),i=n(6348),a=n(9822),s=n(9564),l=n(1117),c=n(4002),u=n(1422),p=n(9690),d=n(9883),f=n(8435),h=n(1668),m=n(9684),g=n(5716),y=n(5184),v=n(5642);t.default=function(e=!1){const t=[f.default,h.default,m.default,g.default,y.default,v.default,c.default,u.default,l.default,p.default,d.default];return e?t.push(o.default,a.default):t.push(r.default,i.default),t.push(s.default),t}},6348:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateTuple=void 0;const r=n(4475),o=n(6124),i=n(8619),a={keyword:"items",type:"array",schemaType:["object","array","boolean"],before:"uniqueItems",code(e){const{schema:t,it:n}=e;if(Array.isArray(t))return s(e,"additionalItems",t);n.items=!0,o.alwaysValidSchema(n,t)||e.ok(i.validateArray(e))}};function s(e,t,n=e.schema){const{gen:i,parentSchema:a,data:s,keyword:l,it:c}=e;!function(e){const{opts:r,errSchemaPath:i}=c,a=n.length,s=a===e.minItems&&(a===e.maxItems||!1===e[t]);if(r.strictTuples&&!s){const e=`"${l}" is ${a}-tuple, but minItems or maxItems/${t} are not specified or different at path "${i}"`;o.checkStrictMode(c,e,r.strictTuples)}}(a),c.opts.unevaluated&&n.length&&!0!==c.items&&(c.items=o.mergeEvaluated.items(i,n.length,c.items));const u=i.name("valid"),p=i.const("len",r._`${s}.length`);n.forEach(((t,n)=>{o.alwaysValidSchema(c,t)||(i.if(r._`${p} > ${n}`,(()=>e.subschema({keyword:l,schemaProp:n,dataProp:n},u))),e.ok(u))}))}t.validateTuple=s,t.default=a},9822:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4475),o=n(6124),i=n(8619),a=n(3074),s={keyword:"items",type:"array",schemaType:["object","boolean"],before:"uniqueItems",error:{message:({params:{len:e}})=>r.str`must NOT have more than ${e} items`,params:({params:{len:e}})=>r._`{limit: ${e}}`},code(e){const{schema:t,parentSchema:n,it:r}=e,{prefixItems:s}=n;r.items=!0,o.alwaysValidSchema(r,t)||(s?a.validateAdditionalItems(e,s):e.ok(i.validateArray(e)))}};t.default=s},8435:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(6124),o={keyword:"not",schemaType:["object","boolean"],trackErrors:!0,code(e){const{gen:t,schema:n,it:o}=e;if(r.alwaysValidSchema(o,n))return void e.fail();const i=t.name("valid");e.subschema({keyword:"not",compositeRule:!0,createErrors:!1,allErrors:!1},i),e.result(i,(()=>e.error()),(()=>e.reset()))},error:{message:"must NOT be valid"}};t.default=o},9684:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4475),o=n(6124),i={keyword:"oneOf",schemaType:"array",trackErrors:!0,error:{message:"must match exactly one schema in oneOf",params:({params:e})=>r._`{passingSchemas: ${e.passing}}`},code(e){const{gen:t,schema:n,parentSchema:i,it:a}=e;if(!Array.isArray(n))throw new Error("ajv implementation error");if(a.opts.discriminator&&i.discriminator)return;const s=n,l=t.let("valid",!1),c=t.let("passing",null),u=t.name("_valid");e.setParams({passing:c}),t.block((function(){s.forEach(((n,i)=>{let s;o.alwaysValidSchema(a,n)?t.var(u,!0):s=e.subschema({keyword:"oneOf",schemaProp:i,compositeRule:!0},u),i>0&&t.if(r._`${u} && ${l}`).assign(l,!1).assign(c,r._`[${c}, ${i}]`).else(),t.if(u,(()=>{t.assign(l,!0),t.assign(c,i),s&&e.mergeEvaluated(s,r.Name)}))}))})),e.result(l,(()=>e.reset()),(()=>e.error(!0)))}};t.default=i},9883:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(8619),o=n(4475),i=n(6124),a=n(6124),s={keyword:"patternProperties",type:"object",schemaType:"object",code(e){const{gen:t,schema:n,data:s,parentSchema:l,it:c}=e,{opts:u}=c,p=r.allSchemaProperties(n),d=p.filter((e=>i.alwaysValidSchema(c,n[e])));if(0===p.length||d.length===p.length&&(!c.opts.unevaluated||!0===c.props))return;const f=u.strictSchema&&!u.allowMatchingProperties&&l.properties,h=t.name("valid");!0===c.props||c.props instanceof o.Name||(c.props=a.evaluatedPropsToName(t,c.props));const{props:m}=c;function g(e){for(const t in f)new RegExp(e).test(t)&&i.checkStrictMode(c,`property ${t} matches pattern ${e} (use allowMatchingProperties)`)}function y(n){t.forIn("key",s,(i=>{t.if(o._`${r.usePattern(e,n)}.test(${i})`,(()=>{const r=d.includes(n);r||e.subschema({keyword:"patternProperties",schemaProp:n,dataProp:i,dataPropType:a.Type.Str},h),c.opts.unevaluated&&!0!==m?t.assign(o._`${m}[${i}]`,!0):r||c.allErrors||t.if(o.not(h),(()=>t.break()))}))}))}!function(){for(const e of p)f&&g(e),c.allErrors?y(e):(t.var(h,!0),y(e),t.if(h))}()}};t.default=s},6988:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(6348),o={keyword:"prefixItems",type:"array",schemaType:["array"],before:"uniqueItems",code:e=>r.validateTuple(e,"items")};t.default=o},9690:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(1321),o=n(8619),i=n(6124),a=n(1422),s={keyword:"properties",type:"object",schemaType:"object",code(e){const{gen:t,schema:n,parentSchema:s,data:l,it:c}=e;("all"===c.opts.removeAdditional&&void 0===s.additionalProperties||!1===c.opts.defaultAdditionalProperties)&&a.default.code(new r.KeywordCxt(c,a.default,"additionalProperties"));const u=o.allSchemaProperties(n);for(const e of u)c.definedProperties.add(e);c.opts.unevaluated&&u.length&&!0!==c.props&&(c.props=i.mergeEvaluated.props(t,i.toHash(u),c.props));const p=u.filter((e=>!i.alwaysValidSchema(c,n[e])));if(0===p.length)return;const d=t.name("valid");for(const n of p)f(n)?h(n):(t.if(o.propertyInData(t,l,n,c.opts.ownProperties)),h(n),c.allErrors||t.else().var(d,!0),t.endIf()),e.it.definedProperties.add(n),e.ok(d);function f(e){return c.opts.useDefaults&&!c.compositeRule&&void 0!==n[e].default}function h(t){e.subschema({keyword:"properties",schemaProp:t,dataProp:t},d)}}};t.default=s},4002:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4475),o=n(6124),i={keyword:"propertyNames",type:"object",schemaType:["object","boolean"],error:{message:"property name must be valid",params:({params:e})=>r._`{propertyName: ${e.propertyName}}`},code(e){const{gen:t,schema:n,data:i,it:a}=e;if(o.alwaysValidSchema(a,n))return;const s=t.name("valid");t.forIn("key",i,(n=>{e.setParams({propertyName:n}),e.subschema({keyword:"propertyNames",data:n,dataTypes:["string"],propertyName:n,compositeRule:!0},s),t.if(r.not(s),(()=>{e.error(!0),a.allErrors||t.break()}))})),e.ok(s)}};t.default=i},5642:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(6124),o={keyword:["then","else"],schemaType:["object","boolean"],code({keyword:e,parentSchema:t,it:n}){void 0===t.if&&r.checkStrictMode(n,`"${e}" without "if" is ignored`)}};t.default=o},8619:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateUnion=t.validateArray=t.usePattern=t.callValidateCode=t.schemaProperties=t.allSchemaProperties=t.noPropertyInData=t.propertyInData=t.isOwnProperty=t.hasPropFunc=t.reportMissingProp=t.checkMissingProp=t.checkReportMissingProp=void 0;const r=n(4475),o=n(6124),i=n(5018);function a(e){return e.scopeValue("func",{ref:Object.prototype.hasOwnProperty,code:r._`Object.prototype.hasOwnProperty`})}function s(e,t,n){return r._`${a(e)}.call(${t}, ${n})`}function l(e,t,n,o){const i=r._`${t}${r.getProperty(n)} === undefined`;return o?r.or(i,r.not(s(e,t,n))):i}function c(e){return e?Object.keys(e).filter((e=>"__proto__"!==e)):[]}t.checkReportMissingProp=function(e,t){const{gen:n,data:o,it:i}=e;n.if(l(n,o,t,i.opts.ownProperties),(()=>{e.setParams({missingProperty:r._`${t}`},!0),e.error()}))},t.checkMissingProp=function({gen:e,data:t,it:{opts:n}},o,i){return r.or(...o.map((o=>r.and(l(e,t,o,n.ownProperties),r._`${i} = ${o}`))))},t.reportMissingProp=function(e,t){e.setParams({missingProperty:t},!0),e.error()},t.hasPropFunc=a,t.isOwnProperty=s,t.propertyInData=function(e,t,n,o){const i=r._`${t}${r.getProperty(n)} !== undefined`;return o?r._`${i} && ${s(e,t,n)}`:i},t.noPropertyInData=l,t.allSchemaProperties=c,t.schemaProperties=function(e,t){return c(t).filter((n=>!o.alwaysValidSchema(e,t[n])))},t.callValidateCode=function({schemaCode:e,data:t,it:{gen:n,topSchemaRef:o,schemaPath:a,errorPath:s},it:l},c,u,p){const d=p?r._`${e}, ${t}, ${o}${a}`:t,f=[[i.default.instancePath,r.strConcat(i.default.instancePath,s)],[i.default.parentData,l.parentData],[i.default.parentDataProperty,l.parentDataProperty],[i.default.rootData,i.default.rootData]];l.opts.dynamicRef&&f.push([i.default.dynamicAnchors,i.default.dynamicAnchors]);const h=r._`${d}, ${n.object(...f)}`;return u!==r.nil?r._`${c}.call(${u}, ${h})`:r._`${c}(${h})`},t.usePattern=function({gen:e,it:{opts:t}},n){const o=t.unicodeRegExp?"u":"";return e.scopeValue("pattern",{key:n,ref:new RegExp(n,o),code:r._`new RegExp(${n}, ${o})`})},t.validateArray=function(e){const{gen:t,data:n,keyword:i,it:a}=e,s=t.name("valid");if(a.allErrors){const e=t.let("valid",!0);return l((()=>t.assign(e,!1))),e}return t.var(s,!0),l((()=>t.break())),s;function l(a){const l=t.const("len",r._`${n}.length`);t.forRange("i",0,l,(n=>{e.subschema({keyword:i,dataProp:n,dataPropType:o.Type.Num},s),t.if(r.not(s),a)}))}},t.validateUnion=function(e){const{gen:t,schema:n,keyword:i,it:a}=e;if(!Array.isArray(n))throw new Error("ajv implementation error");if(n.some((e=>o.alwaysValidSchema(a,e)))&&!a.opts.unevaluated)return;const s=t.let("valid",!1),l=t.name("_valid");t.block((()=>n.forEach(((n,o)=>{const a=e.subschema({keyword:i,schemaProp:o,compositeRule:!0},l);t.assign(s,r._`${s} || ${l}`),e.mergeValidEvaluated(a,l)||t.if(r.not(s))})))),e.result(s,(()=>e.reset()),(()=>e.error(!0)))}},5060:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n={keyword:"id",code(){throw new Error('NOT SUPPORTED: keyword "id", use "$id" for schema ID')}};t.default=n},8223:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(5060),o=n(4028),i=["$schema","$id","$defs","$vocabulary",{keyword:"$comment"},"definitions",r.default,o.default];t.default=i},4028:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.callRef=t.getValidate=void 0;const r=n(4143),o=n(8619),i=n(4475),a=n(5018),s=n(7805),l=n(6124),c={keyword:"$ref",schemaType:"string",code(e){const{gen:t,schema:n,it:o}=e,{baseId:a,schemaEnv:l,validateName:c,opts:d,self:f}=o,{root:h}=l;if(("#"===n||"#/"===n)&&a===h.baseId)return function(){if(l===h)return p(e,c,l,l.$async);const n=t.scopeValue("root",{ref:h});return p(e,i._`${n}.validate`,h,h.$async)}();const m=s.resolveRef.call(f,h,a,n);if(void 0===m)throw new r.default(a,n);return m instanceof s.SchemaEnv?function(t){const n=u(e,t);p(e,n,t,t.$async)}(m):function(r){const o=t.scopeValue("schema",!0===d.code.source?{ref:r,code:i.stringify(r)}:{ref:r}),a=t.name("valid"),s=e.subschema({schema:r,dataTypes:[],schemaPath:i.nil,topSchemaRef:o,errSchemaPath:n},a);e.mergeEvaluated(s),e.ok(a)}(m)}};function u(e,t){const{gen:n}=e;return t.validate?n.scopeValue("validate",{ref:t.validate}):i._`${n.scopeValue("wrapper",{ref:t})}.validate`}function p(e,t,n,r){const{gen:s,it:c}=e,{allErrors:u,schemaEnv:p,opts:d}=c,f=d.passContext?a.default.this:i.nil;function h(e){const t=i._`${e}.errors`;s.assign(a.default.vErrors,i._`${a.default.vErrors} === null ? ${t} : ${a.default.vErrors}.concat(${t})`),s.assign(a.default.errors,i._`${a.default.vErrors}.length`)}function m(e){var t;if(!c.opts.unevaluated)return;const r=null===(t=null==n?void 0:n.validate)||void 0===t?void 0:t.evaluated;if(!0!==c.props)if(r&&!r.dynamicProps)void 0!==r.props&&(c.props=l.mergeEvaluated.props(s,r.props,c.props));else{const t=s.var("props",i._`${e}.evaluated.props`);c.props=l.mergeEvaluated.props(s,t,c.props,i.Name)}if(!0!==c.items)if(r&&!r.dynamicItems)void 0!==r.items&&(c.items=l.mergeEvaluated.items(s,r.items,c.items));else{const t=s.var("items",i._`${e}.evaluated.items`);c.items=l.mergeEvaluated.items(s,t,c.items,i.Name)}}r?function(){if(!p.$async)throw new Error("async schema referenced by sync schema");const n=s.let("valid");s.try((()=>{s.code(i._`await ${o.callValidateCode(e,t,f)}`),m(t),u||s.assign(n,!0)}),(e=>{s.if(i._`!(${e} instanceof ${c.ValidationError})`,(()=>s.throw(e))),h(e),u||s.assign(n,!1)})),e.ok(n)}():function(){const n=s.name("visitedNodes");s.code(i._`const ${n} = visitedNodesForRef.get(${t}) || new Set()`),s.if(i._`!${n}.has(${e.data})`,(()=>{s.code(i._`visitedNodesForRef.set(${t}, ${n})`),s.code(i._`const dataNode = ${e.data}`),s.code(i._`${n}.add(dataNode)`);const r=e.result(o.callValidateCode(e,t,f),(()=>m(t)),(()=>h(t)));return s.code(i._`${n}.delete(dataNode)`),r}))}()}t.getValidate=u,t.callRef=p,t.default=c},5522:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4475),o=n(6545),i={keyword:"discriminator",type:"object",schemaType:"object",error:{message:({params:{discrError:e,tagName:t}})=>e===o.DiscrError.Tag?`tag "${t}" must be string`:`value of tag "${t}" must be in oneOf`,params:({params:{discrError:e,tag:t,tagName:n}})=>r._`{error: ${e}, tag: ${n}, tagValue: ${t}}`},code(e){const{gen:t,data:n,schema:i,parentSchema:a,it:s}=e,{oneOf:l}=a;if(!s.opts.discriminator)throw new Error("discriminator: requires discriminator option");const c=i.propertyName;if("string"!=typeof c)throw new Error("discriminator: requires propertyName");if(!l)throw new Error("discriminator: requires oneOf keyword");const u=t.let("valid",!1),p=t.const("tag",r._`${n}${r.getProperty(c)}`);function d(n){const o=t.name("valid"),i=e.subschema({keyword:"oneOf",schemaProp:n},o);return e.mergeEvaluated(i,r.Name),o}function f(e){return e.hasOwnProperty("$ref")}t.if(r._`typeof ${p} == "string"`,(()=>function(){const n=function(){var e;const t={},n=o(a);let r=!0;for(let t=0;te.error(!1,{discrError:o.DiscrError.Tag,tag:p,tagName:c}))),e.ok(u)}};t.default=i},6545:function(e,t){"use strict";var n;Object.defineProperty(t,"__esModule",{value:!0}),t.DiscrError=void 0,(n=t.DiscrError||(t.DiscrError={})).Tag="tag",n.Mapping="mapping"},6479:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(8223),o=n(3799),i=n(9616),a=n(3815),s=n(4826),l=[r.default,o.default,i.default(),a.default,s.metadataVocabulary,s.contentVocabulary];t.default=l},157:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4475),o={keyword:"format",type:["number","string"],schemaType:"string",$data:!0,error:{message:({schemaCode:e})=>r.str`must match format "${e}"`,params:({schemaCode:e})=>r._`{format: ${e}}`},code(e,t){const{gen:n,data:o,$data:i,schema:a,schemaCode:s,it:l}=e,{opts:c,errSchemaPath:u,schemaEnv:p,self:d}=l;c.validateFormats&&(i?function(){const i=n.scopeValue("formats",{ref:d.formats,code:c.code.formats}),a=n.const("fDef",r._`${i}[${s}]`),l=n.let("fType"),u=n.let("format");n.if(r._`typeof ${a} == "object" && !(${a} instanceof RegExp)`,(()=>n.assign(l,r._`${a}.type || "string"`).assign(u,r._`${a}.validate`)),(()=>n.assign(l,r._`"string"`).assign(u,a))),e.fail$data(r.or(!1===c.strictSchema?r.nil:r._`${s} && !${u}`,function(){const e=p.$async?r._`(${a}.async ? await ${u}(${o}) : ${u}(${o}))`:r._`${u}(${o})`,n=r._`(typeof ${u} == "function" ? ${e} : ${u}.test(${o}))`;return r._`${u} && ${u} !== true && ${l} === ${t} && !${n}`}()))}():function(){const i=d.formats[a];if(!i)return void function(){if(!1!==c.strictSchema)throw new Error(e());function e(){return`unknown format "${a}" ignored in schema at path "${u}"`}d.logger.warn(e())}();if(!0===i)return;const[s,l,f]=function(e){const t=e instanceof RegExp?r.regexpCode(e):c.code.formats?r._`${c.code.formats}${r.getProperty(a)}`:void 0,o=n.scopeValue("formats",{key:a,ref:e,code:t});return"object"!=typeof e||e instanceof RegExp?["string",e,o]:[e.type||"string",e.validate,r._`${o}.validate`]}(i);s===t&&e.pass(function(){if("object"==typeof i&&!(i instanceof RegExp)&&i.async){if(!p.$async)throw new Error("async format in sync schema");return r._`await ${f}(${o})`}return"function"==typeof l?r._`${f}(${o})`:r._`${f}.test(${o})`}())}())}};t.default=o},3815:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=[n(157).default];t.default=r},4826:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.contentVocabulary=t.metadataVocabulary=void 0,t.metadataVocabulary=["title","description","default","deprecated","readOnly","writeOnly","examples"],t.contentVocabulary=["contentMediaType","contentEncoding","contentSchema"]},7535:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4475),o=n(6124),i=n(412),a={keyword:"const",$data:!0,error:{message:"must be equal to constant",params:({schemaCode:e})=>r._`{allowedValue: ${e}}`},code(e){const{gen:t,data:n,$data:a,schemaCode:s,schema:l}=e;a||l&&"object"==typeof l?e.fail$data(r._`!${o.useFunc(t,i.default)}(${n}, ${s})`):e.fail(r._`${l} !== ${n}`)}};t.default=a},4147:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4475),o=n(6124),i=n(412),a={keyword:"enum",schemaType:"array",$data:!0,error:{message:"must be equal to one of the allowed values",params:({schemaCode:e})=>r._`{allowedValues: ${e}}`},code(e){const{gen:t,data:n,$data:a,schema:s,schemaCode:l,it:c}=e;if(!a&&0===s.length)throw new Error("enum must have non-empty array");const u=s.length>=c.opts.loopEnum,p=o.useFunc(t,i.default);let d;if(u||a)d=t.let("valid"),e.block$data(d,(function(){t.assign(d,!1),t.forOf("v",l,(e=>t.if(r._`${p}(${n}, ${e})`,(()=>t.assign(d,!0).break()))))}));else{if(!Array.isArray(s))throw new Error("ajv implementation error");const e=t.const("vSchema",l);d=r.or(...s.map(((t,o)=>function(e,t){const o=s[t];return"object"==typeof o&&null!==o?r._`${p}(${n}, ${e}[${t}])`:r._`${n} === ${o}`}(e,o))))}e.pass(d)}};t.default=a},3799:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(9640),o=n(7692),i=n(3765),a=n(8582),s=n(6711),l=n(7835),c=n(8950),u=n(7326),p=n(7535),d=n(4147),f=[r.default,o.default,i.default,a.default,s.default,l.default,c.default,u.default,{keyword:"type",schemaType:["string","array"]},{keyword:"nullable",schemaType:"boolean"},p.default,d.default];t.default=f},8950:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4475),o={keyword:["maxItems","minItems"],type:"array",schemaType:"number",$data:!0,error:{message({keyword:e,schemaCode:t}){const n="maxItems"===e?"more":"fewer";return r.str`must NOT have ${n} than ${t} items`},params:({schemaCode:e})=>r._`{limit: ${e}}`},code(e){const{keyword:t,data:n,schemaCode:o}=e,i="maxItems"===t?r.operators.GT:r.operators.LT;e.fail$data(r._`${n}.length ${i} ${o}`)}};t.default=o},3765:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4475),o=n(6124),i=n(5872),a={keyword:["maxLength","minLength"],type:"string",schemaType:"number",$data:!0,error:{message({keyword:e,schemaCode:t}){const n="maxLength"===e?"more":"fewer";return r.str`must NOT have ${n} than ${t} characters`},params:({schemaCode:e})=>r._`{limit: ${e}}`},code(e){const{keyword:t,data:n,schemaCode:a,it:s}=e,l="maxLength"===t?r.operators.GT:r.operators.LT,c=!1===s.opts.unicode?r._`${n}.length`:r._`${o.useFunc(e.gen,i.default)}(${n})`;e.fail$data(r._`${c} ${l} ${a}`)}};t.default=a},9640:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4475),o=r.operators,i={maximum:{okStr:"<=",ok:o.LTE,fail:o.GT},minimum:{okStr:">=",ok:o.GTE,fail:o.LT},exclusiveMaximum:{okStr:"<",ok:o.LT,fail:o.GTE},exclusiveMinimum:{okStr:">",ok:o.GT,fail:o.LTE}},a={message:({keyword:e,schemaCode:t})=>r.str`must be ${i[e].okStr} ${t}`,params:({keyword:e,schemaCode:t})=>r._`{comparison: ${i[e].okStr}, limit: ${t}}`},s={keyword:Object.keys(i),type:"number",schemaType:"number",$data:!0,error:a,code(e){const{keyword:t,data:n,schemaCode:o}=e;e.fail$data(r._`${n} ${i[t].fail} ${o} || isNaN(${n})`)}};t.default=s},6711:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4475),o={keyword:["maxProperties","minProperties"],type:"object",schemaType:"number",$data:!0,error:{message({keyword:e,schemaCode:t}){const n="maxProperties"===e?"more":"fewer";return r.str`must NOT have ${n} than ${t} items`},params:({schemaCode:e})=>r._`{limit: ${e}}`},code(e){const{keyword:t,data:n,schemaCode:o}=e,i="maxProperties"===t?r.operators.GT:r.operators.LT;e.fail$data(r._`Object.keys(${n}).length ${i} ${o}`)}};t.default=o},7692:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(4475),o={keyword:"multipleOf",type:"number",schemaType:"number",$data:!0,error:{message:({schemaCode:e})=>r.str`must be multiple of ${e}`,params:({schemaCode:e})=>r._`{multipleOf: ${e}}`},code(e){const{gen:t,data:n,schemaCode:o,it:i}=e,a=i.opts.multipleOfPrecision,s=t.let("res"),l=a?r._`Math.abs(Math.round(${s}) - ${s}) > 1e-${a}`:r._`${s} !== parseInt(${s})`;e.fail$data(r._`(${o} === 0 || (${s} = ${n}/${o}, ${l}))`)}};t.default=o},8582:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(8619),o=n(4475),i={keyword:"pattern",type:"string",schemaType:"string",$data:!0,error:{message:({schemaCode:e})=>o.str`must match pattern "${e}"`,params:({schemaCode:e})=>o._`{pattern: ${e}}`},code(e){const{data:t,$data:n,schema:i,schemaCode:a,it:s}=e,l=s.opts.unicodeRegExp?"u":"",c=n?o._`(new RegExp(${a}, ${l}))`:r.usePattern(e,i);e.fail$data(o._`!${c}.test(${t})`)}};t.default=i},7835:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(8619),o=n(4475),i=n(6124),a={keyword:"required",type:"object",schemaType:"array",$data:!0,error:{message:({params:{missingProperty:e}})=>o.str`must have required property '${e}'`,params:({params:{missingProperty:e}})=>o._`{missingProperty: ${e}}`},code(e){const{gen:t,schema:n,schemaCode:a,data:s,$data:l,it:c}=e,{opts:u}=c;if(!l&&0===n.length)return;const p=n.length>=u.loopRequired;if(c.allErrors?function(){if(p||l)e.block$data(o.nil,d);else for(const t of n)r.checkReportMissingProp(e,t)}():function(){const i=t.let("missing");if(p||l){const n=t.let("valid",!0);e.block$data(n,(()=>function(n,i){e.setParams({missingProperty:n}),t.forOf(n,a,(()=>{t.assign(i,r.propertyInData(t,s,n,u.ownProperties)),t.if(o.not(i),(()=>{e.error(),t.break()}))}),o.nil)}(i,n))),e.ok(n)}else t.if(r.checkMissingProp(e,n,i)),r.reportMissingProp(e,i),t.else()}(),u.strictRequired){const t=e.parentSchema.properties,{definedProperties:r}=e.it;for(const e of n)if(void 0===(null==t?void 0:t[e])&&!r.has(e)){const t=`required property "${e}" is not defined at "${c.schemaEnv.baseId+c.errSchemaPath}" (strictRequired)`;i.checkStrictMode(c,t,c.opts.strictRequired)}}function d(){t.forOf("prop",a,(n=>{e.setParams({missingProperty:n}),t.if(r.noPropertyInData(t,s,n,u.ownProperties),(()=>e.error()))}))}}};t.default=a},7326:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const r=n(7927),o=n(4475),i=n(6124),a=n(412),s={keyword:"uniqueItems",type:"array",schemaType:"boolean",$data:!0,error:{message:({params:{i:e,j:t}})=>o.str`must NOT have duplicate items (items ## ${t} and ${e} are identical)`,params:({params:{i:e,j:t}})=>o._`{i: ${e}, j: ${t}}`},code(e){const{gen:t,data:n,$data:s,schema:l,parentSchema:c,schemaCode:u,it:p}=e;if(!s&&!l)return;const d=t.let("valid"),f=c.items?r.getSchemaTypes(c.items):[];function h(i,a){const s=t.name("item"),l=r.checkDataTypes(f,s,p.opts.strictNumbers,r.DataType.Wrong),c=t.const("indices",o._`{}`);t.for(o._`;${i}--;`,(()=>{t.let(s,o._`${n}[${i}]`),t.if(l,o._`continue`),f.length>1&&t.if(o._`typeof ${s} == "string"`,o._`${s} += "_"`),t.if(o._`typeof ${c}[${s}] == "number"`,(()=>{t.assign(a,o._`${c}[${s}]`),e.error(),t.assign(d,!1).break()})).code(o._`${c}[${s}] = ${i}`)}))}function m(r,s){const l=i.useFunc(t,a.default),c=t.name("outer");t.label(c).for(o._`;${r}--;`,(()=>t.for(o._`${s} = ${r}; ${s}--;`,(()=>t.if(o._`${l}(${n}[${r}], ${n}[${s}])`,(()=>{e.error(),t.assign(d,!1).break(c)}))))))}e.block$data(d,(function(){const r=t.let("i",o._`${n}.length`),i=t.let("j");e.setParams({i:r,j:i}),t.assign(d,!0),t.if(o._`${r} > 1`,(()=>(f.length>0&&!f.some((e=>"object"===e||"array"===e))?h:m)(r,i)))}),o._`${u} === false`),e.ok(d)}};t.default=s},4029:function(e){"use strict";var t=e.exports=function(e,t,r){"function"==typeof t&&(r=t,t={}),n(t,"function"==typeof(r=t.cb||r)?r:r.pre||function(){},r.post||function(){},e,"",e)};function n(e,r,o,i,a,s,l,c,u,p){if(i&&"object"==typeof i&&!Array.isArray(i)){for(var d in r(i,a,s,l,c,u,p),i){var f=i[d];if(Array.isArray(f)){if(d in t.arrayKeywords)for(var h=0;hn.addProblemToIgnore(e))),fileDependencies:o.getFiles(),rootType:S.DefinitionRoot,refTypes:A.refTypes,visitorsData:A.visitorsData}}))}function k(e,t){switch(t){case d.OasMajorVersion.Version3:switch(e){case"Schema":return"schemas";case"Parameter":return"parameters";case"Response":return"responses";case"Example":return"examples";case"RequestBody":return"requestBodies";case"Header":return"headers";case"SecuritySchema":return"securitySchemes";case"Link":return"links";case"Callback":return"callbacks";default:return null}case d.OasMajorVersion.Version2:switch(e){case"Schema":return"definitions";case"Parameter":return"parameters";case"Response":return"responses";default:return null}}}function _(e,t,n,r,a,s){let l;const c={ref:{leave(o,l,c){if(!c.location||void 0===c.node)return void m.reportUnresolvedRef(c,l.report,l.location);if(c.location.source===r.source&&c.location.source===l.location.source&&"scalar"!==l.type.name&&!t)return;if(n&&y.isRedoclyRegistryURL(o.$ref))return;if(s&&f.isAbsoluteUrl(o.$ref))return;const d=k(l.type.name,e);d?t?(p(d,c,l),u(o,c,l)):(o.$ref=p(d,c,l),function(e,t,n){const o=i.makeRefId(n.location.source.absoluteRef,e.$ref);a.set(o,{document:r,isRemote:!1,node:t.node,nodePointer:e.$ref,resolved:!0})}(o,c,l)):u(o,c,l)}},DefinitionRoot:{enter(t){e===d.OasMajorVersion.Version3?l=t.components=t.components||{}:e===d.OasMajorVersion.Version2&&(l=t)}}};function u(e,t,n){g.isPlainObject(t.node)?(delete e.$ref,Object.assign(e,t.node)):n.parent[n.key]=t.node}function p(t,n,r){l[t]=l[t]||{};const o=function(e,t,n){const[r,o]=[e.location.source.absoluteRef,e.location.pointer],i=l[t];let a="";const s=o.slice(2).split("/").filter(Boolean);for(;s.length>0;)if(a=s.pop()+(a?`-${a}`:""),!i||!i[a]||h(i[a],e,n))return a;if(a=f.refBaseName(r)+(a?`_${a}`:""),!i[a]||h(i[a],e,n))return a;const c=a;let u=2;for(;i[a]&&!h(i[a],e,n);)a=`${c}-${u}`,u++;return i[a]||n.report({message:`Two schemas are referenced with the same name but different content. Renamed ${c} to ${a}.`,location:n.location,forceSeverity:"warn"}),a}(n,t,r);return l[t][o]=n.node,e===d.OasMajorVersion.Version3?`#/components/${t}/${o}`:`#/${t}/${o}`}function h(e,t,n){var r;return!(!f.isRef(e)||(null===(r=n.resolve(e).location)||void 0===r?void 0:r.absolutePointer)!==t.location.absolutePointer)||o(e,t.node)}return e===d.OasMajorVersion.Version3&&(c.DiscriminatorMapping={leave(n,r){for(const o of Object.keys(n)){const i=n[o],a=r.resolve({$ref:i});if(!a.location||void 0===a.node)return void m.reportUnresolvedRef(a,r.report,r.location.child(o));const s=k("Schema",e);t?p(s,a,r):n[o]=p(s,a,r)}}}),c}!function(e){e.Version2="oas2",e.Version3_0="oas3_0",e.Version3_1="oas3_1"}(w=t.OasVersion||(t.OasVersion={})),t.bundle=function(e){return r(this,void 0,void 0,(function*(){const{ref:t,doc:n,externalRefResolver:r=new i.BaseResolver(e.config.resolve),base:o=null}=e;if(!t&&!n)throw new Error("Document or reference is required.\n");const a=void 0!==n?n:yield r.resolveDocument(o,t,!0);if(a instanceof Error)throw a;return x(Object.assign(Object.assign({document:a},e),{config:e.config.lint,externalRefResolver:r}))}))},t.bundleDocument=x,t.mapTypeToComponent=k},6877:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={rules:{"info-description":"error","info-contact":"error","info-license":"error","info-license-url":"error","tag-description":"error","tags-alphabetical":"error","parameter-description":"error","no-identical-paths":"error","no-ambiguous-paths":"error","no-path-trailing-slash":"error","path-segment-plural":"error","path-declaration-must-exist":"error","path-not-include-query":"error","path-parameters-defined":"error","operation-description":"error","operation-2xx-response":"error","operation-4xx-response":"error",assertions:"error","operation-operationId":"error","operation-summary":"error","operation-operationId-unique":"error","operation-operationId-url-safe":"error","operation-parameters-unique":"error","operation-tag-defined":"error","operation-security-defined":"error","operation-singular-tag":"error","no-unresolved-refs":"error","no-enum-type-mismatch":"error","boolean-parameter-prefixes":"error","paths-kebab-case":"error","no-http-verbs-in-paths":"error","path-excludes-patterns":{severity:"error",patterns:[]},"request-mime-type":"error",spec:"error","no-invalid-schema-examples":"error","no-invalid-parameter-examples":"error","scalar-property-missing-example":"error"},oas3_0Rules:{"no-invalid-media-type-examples":"error","no-server-example.com":"error","no-server-trailing-slash":"error","no-empty-servers":"error","no-example-value-and-externalValue":"error","no-unused-components":"error","no-undefined-server-variable":"error","no-servers-empty-enum":"error"},oas3_1Rules:{"no-server-example.com":"error","no-server-trailing-slash":"error","no-empty-servers":"error","no-example-value-and-externalValue":"error","no-unused-components":"error","no-undefined-server-variable":"error","no-servers-empty-enum":"error"}}},6242:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.defaultPlugin=t.builtInConfigs=void 0;const r=n(8057),o=n(6877),i=n(9016),a=n(226),s=n(7523),l=n(226),c=n(7523),u=n(1753),p=n(7060);t.builtInConfigs={recommended:r.default,minimal:i.default,all:o.default,"redocly-registry":{decorators:{"registry-dependencies":"on"}}},t.defaultPlugin={id:"",rules:{oas3:a.rules,oas2:s.rules},preprocessors:{oas3:l.preprocessors,oas2:c.preprocessors},decorators:{oas3:u.decorators,oas2:p.decorators},configs:t.builtInConfigs}},7040:function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,i){function a(e){try{l(r.next(e))}catch(e){i(e)}}function s(e){try{l(r.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}l((r=r.apply(e,t||[])).next())}))},o=this&&this.__rest||function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o{if(p.isString(e)&&s.isAbsoluteUrl(e))throw new Error(a.red("We don't support remote plugins yet."));const o=p.isString(e)?n(i.resolve(i.dirname(t),e)):e,l=o.id;if("string"!=typeof l)throw new Error(a.red(`Plugin must define \`id\` property in ${a.blue(e.toString())}.`));if(r.has(l)){const t=r.get(l);throw new Error(a.red(`Plugin "id" must be unique. Plugin ${a.blue(e.toString())} uses id "${a.blue(l)}" already seen in ${a.blue(t)}`))}r.set(l,e.toString());const c=Object.assign(Object.assign({id:l},o.configs?{configs:o.configs}:{}),o.typeExtension?{typeExtension:o.typeExtension}:{});if(o.rules){if(!o.rules.oas3&&!o.rules.oas2)throw new Error(`Plugin rules must have \`oas3\` or \`oas2\` rules "${e}.`);c.rules={},o.rules.oas3&&(c.rules.oas3=u.prefixRules(o.rules.oas3,l)),o.rules.oas2&&(c.rules.oas2=u.prefixRules(o.rules.oas2,l))}if(o.preprocessors){if(!o.preprocessors.oas3&&!o.preprocessors.oas2)throw new Error(`Plugin \`preprocessors\` must have \`oas3\` or \`oas2\` preprocessors "${e}.`);c.preprocessors={},o.preprocessors.oas3&&(c.preprocessors.oas3=u.prefixRules(o.preprocessors.oas3,l)),o.preprocessors.oas2&&(c.preprocessors.oas2=u.prefixRules(o.preprocessors.oas2,l))}if(o.decorators){if(!o.decorators.oas3&&!o.decorators.oas2)throw new Error(`Plugin \`decorators\` must have \`oas3\` or \`oas2\` decorators "${e}.`);c.decorators={},o.decorators.oas3&&(c.decorators.oas3=u.prefixRules(o.decorators.oas3,l)),o.decorators.oas2&&(c.decorators.oas2=u.prefixRules(o.decorators.oas2,l))}return c})).filter(p.notUndefined)}function h({rawConfig:e,configPath:t="",resolver:n}){var o,i;return r(this,void 0,void 0,(function*(){const{apis:r={},lint:a={}}=e;let s={};for(const[e,l]of Object.entries(r||{})){if(null===(i=null===(o=l.lint)||void 0===o?void 0:o.extends)||void 0===i?void 0:i.some(p.isNotString))throw new Error("Error configuration format not detected in extends value must contain strings");const r=v(a,l.lint),c=yield g({lintConfig:r,configPath:t,resolver:n});s[e]=Object.assign(Object.assign({},l),{lint:c})}return s}))}function m({lintConfig:e,configPath:t="",resolver:n=new l.BaseResolver},a=[],d=[]){var h,g,v;return r(this,void 0,void 0,(function*(){if(a.includes(t))throw new Error(`Circular dependency in config file: "${t}"`);const l=u.getUniquePlugins(f([...(null==e?void 0:e.plugins)||[],c.defaultPlugin],t)),b=null===(h=null==e?void 0:e.plugins)||void 0===h?void 0:h.filter(p.isString).map((e=>i.resolve(i.dirname(t),e))),w=s.isAbsoluteUrl(t)?t:t&&i.resolve(t),x=yield Promise.all((null===(g=null==e?void 0:e.extends)||void 0===g?void 0:g.map((e=>r(this,void 0,void 0,(function*(){if(!s.isAbsoluteUrl(e)&&!i.extname(e))return y(e,l);const o=s.isAbsoluteUrl(e)?e:s.isAbsoluteUrl(t)?new URL(e,t).href:i.resolve(i.dirname(t),e),c=yield function(e,t){return r(this,void 0,void 0,(function*(){try{const n=yield t.loadExternalRef(e),r=u.transformConfig(p.parseYaml(n.body));if(!r.lint)throw new Error(`Lint configuration format not detected: "${e}"`);return r.lint}catch(t){throw new Error(`Failed to load "${e}": ${t.message}`)}}))}(o,n);return yield m({lintConfig:c,configPath:o,resolver:n},[...a,w],d)})))))||[]),k=u.mergeExtends([...x,Object.assign(Object.assign({},e),{plugins:l,extends:void 0,extendPaths:[...a,w],pluginPaths:b})]),{plugins:_=[]}=k,O=o(k,["plugins"]);return Object.assign(Object.assign({},O),{extendPaths:null===(v=O.extendPaths)||void 0===v?void 0:v.filter((e=>e&&!s.isAbsoluteUrl(e))),plugins:u.getUniquePlugins(_),recommendedFallback:null==e?void 0:e.recommendedFallback,doNotResolveExamples:null==e?void 0:e.doNotResolveExamples})}))}function g(e,t=[],n=[]){return r(this,void 0,void 0,(function*(){const r=yield m(e,t,n);return Object.assign(Object.assign({},r),{rules:r.rules&&b(r.rules)})}))}function y(e,t){var n;const{pluginId:r,configName:o}=u.parsePresetName(e),i=t.find((e=>e.id===r));if(!i)throw new Error(`Invalid config ${a.red(e)}: plugin ${r} is not included.`);const s=null===(n=i.configs)||void 0===n?void 0:n[o];if(!s)throw new Error(r?`Invalid config ${a.red(e)}: plugin ${r} doesn't export config with name ${o}.`:`Invalid config ${a.red(e)}: there is no such built-in config.`);return s}function v(e,t){return Object.assign(Object.assign(Object.assign({},e),t),{rules:Object.assign(Object.assign({},null==e?void 0:e.rules),null==t?void 0:t.rules),oas2Rules:Object.assign(Object.assign({},null==e?void 0:e.oas2Rules),null==t?void 0:t.oas2Rules),oas3_0Rules:Object.assign(Object.assign({},null==e?void 0:e.oas3_0Rules),null==t?void 0:t.oas3_0Rules),oas3_1Rules:Object.assign(Object.assign({},null==e?void 0:e.oas3_1Rules),null==t?void 0:t.oas3_1Rules),preprocessors:Object.assign(Object.assign({},null==e?void 0:e.preprocessors),null==t?void 0:t.preprocessors),oas2Preprocessors:Object.assign(Object.assign({},null==e?void 0:e.oas2Preprocessors),null==t?void 0:t.oas2Preprocessors),oas3_0Preprocessors:Object.assign(Object.assign({},null==e?void 0:e.oas3_0Preprocessors),null==t?void 0:t.oas3_0Preprocessors),oas3_1Preprocessors:Object.assign(Object.assign({},null==e?void 0:e.oas3_1Preprocessors),null==t?void 0:t.oas3_1Preprocessors),decorators:Object.assign(Object.assign({},null==e?void 0:e.decorators),null==t?void 0:t.decorators),oas2Decorators:Object.assign(Object.assign({},null==e?void 0:e.oas2Decorators),null==t?void 0:t.oas2Decorators),oas3_0Decorators:Object.assign(Object.assign({},null==e?void 0:e.oas3_0Decorators),null==t?void 0:t.oas3_0Decorators),oas3_1Decorators:Object.assign(Object.assign({},null==e?void 0:e.oas3_1Decorators),null==t?void 0:t.oas3_1Decorators),recommendedFallback:!(null==t?void 0:t.extends)&&e.recommendedFallback})}function b(e){if(!e)return e;const t={},n=[];for(const[r,o]of Object.entries(e))if(r.startsWith("assert/")&&"object"==typeof o&&null!==o){const e=o;n.push(Object.assign(Object.assign({},e),{assertionId:r.replace("assert/","")}))}else t[r]=o;return n.length>0&&(t.assertions=n),t}t.resolveConfig=function(e,t){var n,o,i,a,s;return r(this,void 0,void 0,(function*(){if(null===(o=null===(n=e.lint)||void 0===n?void 0:n.extends)||void 0===o?void 0:o.some(p.isNotString))throw new Error("Error configuration format not detected in extends value must contain strings");const r=new l.BaseResolver(u.getResolveConfig(e.resolve)),c=null!==(a=null===(i=null==e?void 0:e.lint)||void 0===i?void 0:i.extends)&&void 0!==a?a:["recommended"],f=!(null===(s=null==e?void 0:e.lint)||void 0===s?void 0:s.extends),m=Object.assign(Object.assign({},null==e?void 0:e.lint),{extends:c,recommendedFallback:f}),y=yield h({rawConfig:Object.assign(Object.assign({},e),{lint:m}),configPath:t,resolver:r}),v=yield g({lintConfig:m,configPath:t,resolver:r});return new d.Config(Object.assign(Object.assign({},e),{apis:y,lint:v}),t)}))},t.resolvePlugins=f,t.resolveApis=h,t.resolveLint=g,t.resolvePreset=y},3777:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Config=t.LintConfig=t.AVAILABLE_REGIONS=t.DOMAINS=t.DEFAULT_REGION=t.IGNORE_FILE=t.env=void 0;const r=n(5101),o=n(6470),i=n(5273),a=n(771),s=n(1510),l=n(2565);t.env="undefined"!=typeof process&&{}||{},t.IGNORE_FILE=".redocly.lint-ignore.yaml",t.DEFAULT_REGION="us",t.DOMAINS=function(){const e={us:"redocly.com",eu:"eu.redocly.com"},n=t.env.REDOCLY_DOMAIN;return(null==n?void 0:n.endsWith(".redocly.host"))&&(e[n.split(".")[0]]=n),"redoc.online"===n&&(e[n]=n),e}(),t.AVAILABLE_REGIONS=Object.keys(t.DOMAINS);class c{constructor(e,n){this.rawConfig=e,this.configFile=n,this.ignore={},this._usedRules=new Set,this._usedVersions=new Set,this.plugins=e.plugins||[],this.doNotResolveExamples=!!e.doNotResolveExamples,this.recommendedFallback=e.recommendedFallback||!1,this.rules={[s.OasVersion.Version2]:Object.assign(Object.assign({},e.rules),e.oas2Rules),[s.OasVersion.Version3_0]:Object.assign(Object.assign({},e.rules),e.oas3_0Rules),[s.OasVersion.Version3_1]:Object.assign(Object.assign({},e.rules),e.oas3_1Rules)},this.preprocessors={[s.OasVersion.Version2]:Object.assign(Object.assign({},e.preprocessors),e.oas2Preprocessors),[s.OasVersion.Version3_0]:Object.assign(Object.assign({},e.preprocessors),e.oas3_0Preprocessors),[s.OasVersion.Version3_1]:Object.assign(Object.assign({},e.preprocessors),e.oas3_1Preprocessors)},this.decorators={[s.OasVersion.Version2]:Object.assign(Object.assign({},e.decorators),e.oas2Decorators),[s.OasVersion.Version3_0]:Object.assign(Object.assign({},e.decorators),e.oas3_0Decorators),[s.OasVersion.Version3_1]:Object.assign(Object.assign({},e.decorators),e.oas3_1Decorators)},this.extendPaths=e.extendPaths||[],this.pluginPaths=e.pluginPaths||[];const a=this.configFile?o.dirname(this.configFile):"undefined"!=typeof process&&process.cwd()||"",l=o.join(a,t.IGNORE_FILE);if(r.hasOwnProperty("existsSync")&&r.existsSync(l)){this.ignore=i.parseYaml(r.readFileSync(l,"utf-8"))||{};for(const e of Object.keys(this.ignore)){this.ignore[o.resolve(o.dirname(l),e)]=this.ignore[e];for(const t of Object.keys(this.ignore[e]))this.ignore[e][t]=new Set(this.ignore[e][t]);delete this.ignore[e]}}}saveIgnore(){const e=this.configFile?o.dirname(this.configFile):process.cwd(),n=o.join(e,t.IGNORE_FILE),s={};for(const t of Object.keys(this.ignore)){const n=s[a.slash(o.relative(e,t))]=this.ignore[t];for(const e of Object.keys(n))n[e]=Array.from(n[e])}r.writeFileSync(n,"# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.\n# See https://redoc.ly/docs/cli/ for more information.\n"+i.stringifyYaml(s))}addIgnore(e){const t=this.ignore,n=e.location[0];if(void 0===n.pointer)return;const r=t[n.source.absoluteRef]=t[n.source.absoluteRef]||{};(r[e.ruleId]=r[e.ruleId]||new Set).add(n.pointer)}addProblemToIgnore(e){const t=e.location[0];if(void 0===t.pointer)return e;const n=(this.ignore[t.source.absoluteRef]||{})[e.ruleId],r=n&&n.has(t.pointer);return r?Object.assign(Object.assign({},e),{ignored:r}):e}extendTypes(e,t){let n=e;for(const e of this.plugins)if(void 0!==e.typeExtension)switch(t){case s.OasVersion.Version3_0:case s.OasVersion.Version3_1:if(!e.typeExtension.oas3)continue;n=e.typeExtension.oas3(n,t);case s.OasVersion.Version2:if(!e.typeExtension.oas2)continue;n=e.typeExtension.oas2(n,t);default:throw new Error("Not implemented")}return n}getRuleSettings(e,t){this._usedRules.add(e),this._usedVersions.add(t);const n=this.rules[t][e]||"off";return"string"==typeof n?{severity:n}:Object.assign({severity:"error"},n)}getPreprocessorSettings(e,t){this._usedRules.add(e),this._usedVersions.add(t);const n=this.preprocessors[t][e]||"off";return"string"==typeof n?{severity:"on"===n?"error":n}:Object.assign({severity:"error"},n)}getDecoratorSettings(e,t){this._usedRules.add(e),this._usedVersions.add(t);const n=this.decorators[t][e]||"off";return"string"==typeof n?{severity:"on"===n?"error":n}:Object.assign({severity:"error"},n)}getUnusedRules(){const e=[],t=[],n=[];for(const r of Array.from(this._usedVersions))e.push(...Object.keys(this.rules[r]).filter((e=>!this._usedRules.has(e)))),t.push(...Object.keys(this.decorators[r]).filter((e=>!this._usedRules.has(e)))),n.push(...Object.keys(this.preprocessors[r]).filter((e=>!this._usedRules.has(e))));return{rules:e,preprocessors:n,decorators:t}}getRulesForOasVersion(e){switch(e){case s.OasMajorVersion.Version3:const e=[];return this.plugins.forEach((t=>{var n;return(null===(n=t.preprocessors)||void 0===n?void 0:n.oas3)&&e.push(t.preprocessors.oas3)})),this.plugins.forEach((t=>{var n;return(null===(n=t.rules)||void 0===n?void 0:n.oas3)&&e.push(t.rules.oas3)})),this.plugins.forEach((t=>{var n;return(null===(n=t.decorators)||void 0===n?void 0:n.oas3)&&e.push(t.decorators.oas3)})),e;case s.OasMajorVersion.Version2:const t=[];return this.plugins.forEach((e=>{var n;return(null===(n=e.preprocessors)||void 0===n?void 0:n.oas2)&&t.push(e.preprocessors.oas2)})),this.plugins.forEach((e=>{var n;return(null===(n=e.rules)||void 0===n?void 0:n.oas2)&&t.push(e.rules.oas2)})),this.plugins.forEach((e=>{var n;return(null===(n=e.decorators)||void 0===n?void 0:n.oas2)&&t.push(e.decorators.oas2)})),t}}skipRules(e){for(const t of e||[])for(const e of Object.values(s.OasVersion))this.rules[e][t]&&(this.rules[e][t]="off")}skipPreprocessors(e){for(const t of e||[])for(const e of Object.values(s.OasVersion))this.preprocessors[e][t]&&(this.preprocessors[e][t]="off")}skipDecorators(e){for(const t of e||[])for(const e of Object.values(s.OasVersion))this.decorators[e][t]&&(this.decorators[e][t]="off")}}t.LintConfig=c,t.Config=class{constructor(e,t){this.rawConfig=e,this.configFile=t,this.apis=e.apis||{},this.lint=new c(e.lint||{},t),this["features.openapi"]=e["features.openapi"]||{},this["features.mockServer"]=e["features.mockServer"]||{},this.resolve=l.getResolveConfig(null==e?void 0:e.resolve),this.region=e.region,this.organization=e.organization}}},8698:function(e,t,n){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,t,n,r){void 0===r&&(r=n),Object.defineProperty(e,r,{enumerable:!0,get:function(){return t[n]}})}:function(e,t,n,r){void 0===r&&(r=n),e[r]=t[n]}),o=this&&this.__exportStar||function(e,t){for(var n in e)"default"===n||Object.prototype.hasOwnProperty.call(t,n)||r(t,e,n)};Object.defineProperty(t,"__esModule",{value:!0}),o(n(3777),t),o(n(3865),t),o(n(5030),t),o(n(6242),t),o(n(9129),t),o(n(2565),t),o(n(7040),t)},9129:function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,i){function a(e){try{l(r.next(e))}catch(e){i(e)}}function s(e){try{l(r.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}l((r=r.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.getConfig=t.findConfig=t.CONFIG_FILE_NAMES=t.loadConfig=void 0;const o=n(5101),i=n(6470),a=n(1094),s=n(771),l=n(3777),c=n(2565),u=n(7040);function p(e){if(!o.hasOwnProperty("existsSync"))return;const n=t.CONFIG_FILE_NAMES.map((t=>e?i.resolve(e,t):t)).filter(o.existsSync);if(n.length>1)throw new Error(`\n Multiple configuration files are not allowed. \n Found the following files: ${n.join(", ")}. \n Please use 'redocly.yaml' instead.\n `);return n[0]}function d(e=p()){return r(this,void 0,void 0,(function*(){if(!e)return{};try{const t=(yield s.loadYaml(e))||{};return c.transformConfig(t)}catch(t){throw new Error(`Error parsing config file at '${e}': ${t.message}`)}}))}t.loadConfig=function(e=p(),t,n){return r(this,void 0,void 0,(function*(){const o=yield d(e);return"function"==typeof n&&(yield n(o)),yield function({rawConfig:e,customExtends:t,configPath:n}){var o;return r(this,void 0,void 0,(function*(){void 0!==t?(e.lint=e.lint||{},e.lint.extends=t):s.isEmptyObject(e);const r=new a.RedoclyClient,i=yield r.getTokens();if(i.length){e.resolve||(e.resolve={}),e.resolve.http||(e.resolve.http={}),e.resolve.http.headers=[...null!==(o=e.resolve.http.headers)&&void 0!==o?o:[]];for(const t of i){const n=l.DOMAINS[t.region];e.resolve.http.headers.push({matches:`https://api.${n}/registry/**`,name:"Authorization",envVariable:void 0,value:t.token},..."us"===t.region?[{matches:"https://api.redoc.ly/registry/**",name:"Authorization",envVariable:void 0,value:t.token}]:[])}}return u.resolveConfig(e,n)}))}({rawConfig:o,customExtends:t,configPath:e})}))},t.CONFIG_FILE_NAMES=["redocly.yaml","redocly.yml",".redocly.yaml",".redocly.yml"],t.findConfig=p,t.getConfig=d},9016:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={rules:{"info-description":"warn","info-contact":"off","info-license":"off","info-license-url":"off","tag-description":"warn","tags-alphabetical":"off","parameter-description":"off","no-path-trailing-slash":"warn","no-identical-paths":"warn","no-ambiguous-paths":"warn","path-declaration-must-exist":"warn","path-not-include-query":"warn","path-parameters-defined":"warn","operation-description":"off","operation-2xx-response":"warn","operation-4xx-response":"off",assertions:"warn","operation-operationId":"warn","operation-summary":"warn","operation-operationId-unique":"warn","operation-parameters-unique":"warn","operation-tag-defined":"off","operation-security-defined":"warn","operation-operationId-url-safe":"warn","operation-singular-tag":"off","no-unresolved-refs":"error","no-enum-type-mismatch":"warn","boolean-parameter-prefixes":"off","paths-kebab-case":"off",spec:"error"},oas3_0Rules:{"no-invalid-media-type-examples":{severity:"warn",disallowAdditionalProperties:!0},"no-server-example.com":"warn","no-server-trailing-slash":"error","no-empty-servers":"warn","no-example-value-and-externalValue":"warn","no-unused-components":"warn","no-undefined-server-variable":"warn","no-servers-empty-enum":"error"},oas3_1Rules:{"no-server-example.com":"warn","no-server-trailing-slash":"error","no-empty-servers":"warn","no-example-value-and-externalValue":"warn","no-unused-components":"warn","no-undefined-server-variable":"warn","no-servers-empty-enum":"error"}}},8057:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={rules:{"info-description":"warn","info-contact":"off","info-license":"warn","info-license-url":"warn","tag-description":"warn","tags-alphabetical":"off","parameter-description":"off","no-path-trailing-slash":"error","no-identical-paths":"error","no-ambiguous-paths":"warn","path-declaration-must-exist":"error","path-not-include-query":"error","path-parameters-defined":"error","operation-description":"off","operation-2xx-response":"warn",assertions:"warn","operation-4xx-response":"warn","operation-operationId":"warn","operation-summary":"error","operation-operationId-unique":"error","operation-operationId-url-safe":"error","operation-parameters-unique":"error","operation-tag-defined":"off","operation-security-defined":"error","operation-singular-tag":"off","no-unresolved-refs":"error","no-enum-type-mismatch":"error","boolean-parameter-prefixes":"off","paths-kebab-case":"off",spec:"error"},oas3_0Rules:{"no-invalid-media-type-examples":{severity:"warn",disallowAdditionalProperties:!0},"no-server-example.com":"warn","no-server-trailing-slash":"error","no-empty-servers":"error","no-example-value-and-externalValue":"error","no-unused-components":"warn","no-undefined-server-variable":"error","no-servers-empty-enum":"error"},oas3_1Rules:{"no-server-example.com":"warn","no-server-trailing-slash":"error","no-empty-servers":"error","no-example-value-and-externalValue":"error","no-unused-components":"warn","no-undefined-server-variable":"error","no-servers-empty-enum":"error"}}},5030:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.initRules=void 0;const r=n(771);t.initRules=function(e,t,n,o){return e.flatMap((e=>Object.keys(e).map((r=>{const i=e[r],a="rules"===n?t.getRuleSettings(r,o):"preprocessors"===n?t.getPreprocessorSettings(r,o):t.getDecoratorSettings(r,o);if("off"===a.severity)return;const s=i(a);return Array.isArray(s)?s.map((e=>({severity:a.severity,ruleId:r,visitor:e}))):{severity:a.severity,ruleId:r,visitor:s}})))).flatMap((e=>e)).filter(r.notUndefined)}},3865:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0})},2565:function(e,t,n){"use strict";var r=this&&this.__rest||function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o-1){const[t,n]=e.split("/");return{pluginId:t,configName:n}}return{pluginId:"",configName:e}},t.transformApiDefinitionsToApis=s,t.prefixRules=function(e,t){if(!t)return e;const n={};for(const r of Object.keys(e))n[`${t}/${r}`]=e[r];return n},t.mergeExtends=function(e){const t={rules:{},oas2Rules:{},oas3_0Rules:{},oas3_1Rules:{},preprocessors:{},oas2Preprocessors:{},oas3_0Preprocessors:{},oas3_1Preprocessors:{},decorators:{},oas2Decorators:{},oas3_0Decorators:{},oas3_1Decorators:{},plugins:[],pluginPaths:[],extendPaths:[]};for(let n of e){if(n.extends)throw new Error(`\`extends\` is not supported in shared configs yet: ${JSON.stringify(n,null,2)}.`);Object.assign(t.rules,n.rules),Object.assign(t.oas2Rules,n.oas2Rules),i.assignExisting(t.oas2Rules,n.rules||{}),Object.assign(t.oas3_0Rules,n.oas3_0Rules),i.assignExisting(t.oas3_0Rules,n.rules||{}),Object.assign(t.oas3_1Rules,n.oas3_1Rules),i.assignExisting(t.oas3_1Rules,n.rules||{}),Object.assign(t.preprocessors,n.preprocessors),Object.assign(t.oas2Preprocessors,n.oas2Preprocessors),i.assignExisting(t.oas2Preprocessors,n.preprocessors||{}),Object.assign(t.oas3_0Preprocessors,n.oas3_0Preprocessors),i.assignExisting(t.oas3_0Preprocessors,n.preprocessors||{}),Object.assign(t.oas3_1Preprocessors,n.oas3_1Preprocessors),i.assignExisting(t.oas3_1Preprocessors,n.preprocessors||{}),Object.assign(t.decorators,n.decorators),Object.assign(t.oas2Decorators,n.oas2Decorators),i.assignExisting(t.oas2Decorators,n.decorators||{}),Object.assign(t.oas3_0Decorators,n.oas3_0Decorators),i.assignExisting(t.oas3_0Decorators,n.decorators||{}),Object.assign(t.oas3_1Decorators,n.oas3_1Decorators),i.assignExisting(t.oas3_1Decorators,n.decorators||{}),t.plugins.push(...n.plugins||[]),t.pluginPaths.push(...n.pluginPaths||[]),t.extendPaths.push(...new Set(n.extendPaths))}return t},t.getMergedConfig=function(e,t){var n,r,o,i,s,l;const c=[...Object.values(e.apis).map((e=>{var t;return null===(t=null==e?void 0:e.lint)||void 0===t?void 0:t.extendPaths})),null===(r=null===(n=e.rawConfig)||void 0===n?void 0:n.lint)||void 0===r?void 0:r.extendPaths].flat().filter(Boolean),u=[...Object.values(e.apis).map((e=>{var t;return null===(t=null==e?void 0:e.lint)||void 0===t?void 0:t.pluginPaths})),null===(i=null===(o=e.rawConfig)||void 0===o?void 0:o.lint)||void 0===i?void 0:i.pluginPaths].flat().filter(Boolean);return t?new a.Config(Object.assign(Object.assign({},e.rawConfig),{lint:Object.assign(Object.assign({},e.apis[t]?e.apis[t].lint:e.rawConfig.lint),{extendPaths:c,pluginPaths:u}),"features.openapi":Object.assign(Object.assign({},e["features.openapi"]),null===(s=e.apis[t])||void 0===s?void 0:s["features.openapi"]),"features.mockServer":Object.assign(Object.assign({},e["features.mockServer"]),null===(l=e.apis[t])||void 0===l?void 0:l["features.mockServer"])}),e.configFile):e},t.transformConfig=function(e){if(e.apis&&e.apiDefinitions)throw new Error("Do not use 'apiDefinitions' field. Use 'apis' instead.\n");if(e["features.openapi"]&&e.referenceDocs)throw new Error("Do not use 'referenceDocs' field. Use 'features.openapi' instead.\n");const t=e,{apiDefinitions:n,referenceDocs:i}=t,a=r(t,["apiDefinitions","referenceDocs"]);return n&&process.stderr.write(`The ${o.yellow("apiDefinitions")} field is deprecated. Use ${o.green("apis")} instead. Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`),i&&process.stderr.write(`The ${o.yellow("referenceDocs")} field is deprecated. Use ${o.green("features.openapi")} instead. Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`),Object.assign({"features.openapi":i,apis:s(n)},a)},t.getResolveConfig=function(e){var t,n;return{http:{headers:null!==(n=null===(t=null==e?void 0:e.http)||void 0===t?void 0:t.headers)&&void 0!==n?n:[],customFetch:void 0}}},t.getUniquePlugins=function(e){const t=new Set,n=[];for(const r of e)t.has(r.id)?r.id&&process.stderr.write(`Duplicate plugin id "${o.yellow(r.id)}".\n`):(n.push(r),t.add(r.id));return n}},1988:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.checkIfMatchByStrategy=t.filter=void 0;const r=n(7468),o=n(771);function i(e){return Array.isArray(e)?e:[e]}t.filter=function(e,t,n){const{parent:i,key:a}=t;let s=!1;if(Array.isArray(e))for(let o=0;oe.includes(t))):"all"===n&&t.every((t=>e.includes(t)))):e===t)}},9244:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.FilterIn=void 0;const r=n(1988);t.FilterIn=({property:e,value:t,matchStrategy:n})=>{const o=n||"any",i=n=>(null==n?void 0:n[e])&&!r.checkIfMatchByStrategy(null==n?void 0:n[e],t,o);return{any:{enter:(e,t)=>{r.filter(e,t,i)}}}}},8623:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.FilterOut=void 0;const r=n(1988);t.FilterOut=({property:e,value:t,matchStrategy:n})=>{const o=n||"any",i=n=>r.checkIfMatchByStrategy(null==n?void 0:n[e],t,o);return{any:{enter:(e,t)=>{r.filter(e,t,i)}}}}},4555:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.InfoDescriptionOverride=void 0;const r=n(771);t.InfoDescriptionOverride=({filePath:e})=>({Info:{leave(t,{report:n,location:o}){if(!e)throw new Error('Parameter "filePath" is not provided for "info-description-override" rule');try{t.description=r.readFileAsStringSync(e)}catch(e){n({message:`Failed to read markdown override file for "info.description".\n${e.message}`,location:o.child("description")})}}}})},7802:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationDescriptionOverride=void 0;const r=n(771);t.OperationDescriptionOverride=({operationIds:e})=>({Operation:{leave(t,{report:n,location:o}){if(!t.operationId)return;if(!e)throw new Error('Parameter "operationIds" is not provided for "operation-description-override" rule');const i=t.operationId;if(e[i])try{t.description=r.readFileAsStringSync(e[i])}catch(e){n({message:`Failed to read markdown override file for operation "${i}".\n${e.message}`,location:o.child("operationId").key()})}}}})},2287:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RegistryDependencies=void 0;const r=n(1094);t.RegistryDependencies=()=>{let e=new Set;return{DefinitionRoot:{leave(t,n){n.getVisitorData().links=Array.from(e)}},ref(t){if(t.$ref){const n=t.$ref.split("#/")[0];r.isRedoclyRegistryURL(n)&&e.add(n)}}}}},5830:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RemoveXInternal=void 0;const r=n(771),o=n(7468);t.RemoveXInternal=({internalFlagProperty:e})=>{const t=e||"x-internal";return{any:{enter:(e,n)=>{!function(e,n){var i,a,s,l;const{parent:c,key:u}=n;let p=!1;if(Array.isArray(e))for(let r=0;r({Tag:{leave(t,{report:n}){if(!e)throw new Error('Parameter "tagNames" is not provided for "tag-description-override" rule');if(e[t.name])try{t.description=r.readFileAsStringSync(e[t.name])}catch(e){n({message:`Failed to read markdown override file for tag "${t.name}".\n${e.message}`})}}}})},7060:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.decorators=void 0;const r=n(2287),o=n(7802),i=n(423),a=n(4555),s=n(5830),l=n(9244),c=n(8623);t.decorators={"registry-dependencies":r.RegistryDependencies,"operation-description-override":o.OperationDescriptionOverride,"tag-description-override":i.TagDescriptionOverride,"info-description-override":a.InfoDescriptionOverride,"remove-x-internal":s.RemoveXInternal,"filter-in":l.FilterIn,"filter-out":c.FilterOut}},1753:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.decorators=void 0;const r=n(2287),o=n(7802),i=n(423),a=n(4555),s=n(5830),l=n(9244),c=n(8623);t.decorators={"registry-dependencies":r.RegistryDependencies,"operation-description-override":o.OperationDescriptionOverride,"tag-description-override":i.TagDescriptionOverride,"info-description-override":a.InfoDescriptionOverride,"remove-x-internal":s.RemoveXInternal,"filter-in":l.FilterIn,"filter-out":c.FilterOut}},5273:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.stringifyYaml=t.parseYaml=void 0;const r=n(3320),o=r.JSON_SCHEMA.extend({implicit:[r.types.merge],explicit:[r.types.binary,r.types.omap,r.types.pairs,r.types.set]});t.parseYaml=(e,t)=>r.load(e,Object.assign({schema:o},t)),t.stringifyYaml=(e,t)=>r.dump(e,t)},1510:function(e,t){"use strict";var n,r;Object.defineProperty(t,"__esModule",{value:!0}),t.openAPIMajor=t.detectOpenAPI=t.OasMajorVersion=t.OasVersion=void 0,function(e){e.Version2="oas2",e.Version3_0="oas3_0",e.Version3_1="oas3_1"}(n=t.OasVersion||(t.OasVersion={})),function(e){e.Version2="oas2",e.Version3="oas3"}(r=t.OasMajorVersion||(t.OasMajorVersion={})),t.detectOpenAPI=function(e){if("object"!=typeof e)throw new Error("Document must be JSON object, got "+typeof e);if(!e.openapi&&!e.swagger)throw new Error("This doesn’t look like an OpenAPI document.\n");if(e.openapi&&"string"!=typeof e.openapi)throw new Error(`Invalid OpenAPI version: should be a string but got "${typeof e.openapi}"`);if(e.openapi&&e.openapi.startsWith("3.0"))return n.Version3_0;if(e.openapi&&e.openapi.startsWith("3.1"))return n.Version3_1;if(e.swagger&&"2.0"===e.swagger)return n.Version2;throw new Error(`Unsupported OpenAPI Version: ${e.openapi||e.swagger}`)},t.openAPIMajor=function(e){return e===n.Version2?r.Version2:r.Version3}},1094:function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,i){function a(e){try{l(r.next(e))}catch(e){i(e)}}function s(e){try{l(r.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}l((r=r.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.isRedoclyRegistryURL=t.RedoclyClient=void 0;const o=n(2116),i=n(6470),a=n(6918),s=n(8836),l=n(1390),c=n(3777),u=n(771),p=".redocly-config.json";t.RedoclyClient=class{constructor(e){this.accessTokens={},this.region=this.loadRegion(e),this.loadTokens(),this.domain=e?c.DOMAINS[e]:c.env.REDOCLY_DOMAIN||c.DOMAINS[c.DEFAULT_REGION],c.env.REDOCLY_DOMAIN=this.domain,this.registryApi=new l.RegistryApi(this.accessTokens,this.region)}loadRegion(e){if(e&&!c.DOMAINS[e])throw new Error(`Invalid argument: region in config file.\nGiven: ${s.green(e)}, choices: "us", "eu".`);return c.env.REDOCLY_DOMAIN?c.AVAILABLE_REGIONS.find((e=>c.DOMAINS[e]===c.env.REDOCLY_DOMAIN))||c.DEFAULT_REGION:e||c.DEFAULT_REGION}getRegion(){return this.region}hasTokens(){return u.isNotEmptyObject(this.accessTokens)}hasToken(){return!!this.accessTokens[this.region]}getAuthorizationHeader(){return r(this,void 0,void 0,(function*(){return this.accessTokens[this.region]}))}setAccessTokens(e){this.accessTokens=e}loadTokens(){const e=i.resolve(a.homedir(),p),t=this.readCredentialsFile(e);u.isNotEmptyObject(t)&&this.setAccessTokens(Object.assign(Object.assign({},t),t.token&&!t[this.region]&&{[this.region]:t.token})),c.env.REDOCLY_AUTHORIZATION&&this.setAccessTokens(Object.assign(Object.assign({},this.accessTokens),{[this.region]:c.env.REDOCLY_AUTHORIZATION}))}getAllTokens(){return Object.entries(this.accessTokens).filter((([e])=>c.AVAILABLE_REGIONS.includes(e))).map((([e,t])=>({region:e,token:t})))}getValidTokens(){return r(this,void 0,void 0,(function*(){const e=this.getAllTokens(),t=yield Promise.allSettled(e.map((({token:e,region:t})=>this.verifyToken(e,t))));return e.filter(((e,n)=>"fulfilled"===t[n].status)).map((({token:e,region:t})=>({token:e,region:t,valid:!0})))}))}getTokens(){return r(this,void 0,void 0,(function*(){return this.hasTokens()?yield this.getValidTokens():[]}))}isAuthorizedWithRedoclyByRegion(){return r(this,void 0,void 0,(function*(){if(!this.hasTokens())return!1;const e=this.accessTokens[this.region];if(!e)return!1;try{return yield this.verifyToken(e,this.region),!0}catch(e){return!1}}))}isAuthorizedWithRedocly(){return r(this,void 0,void 0,(function*(){return this.hasTokens()&&u.isNotEmptyObject(yield this.getValidTokens())}))}readCredentialsFile(e){return o.existsSync(e)?JSON.parse(o.readFileSync(e,"utf-8")):{}}verifyToken(e,t,n=!1){return r(this,void 0,void 0,(function*(){return this.registryApi.authStatus(e,t,n)}))}login(e,t=!1){return r(this,void 0,void 0,(function*(){const n=i.resolve(a.homedir(),p);try{yield this.verifyToken(e,this.region,t)}catch(e){throw new Error("Authorization failed. Please check if you entered a valid API key.")}const r=Object.assign(Object.assign({},this.readCredentialsFile(n)),{[this.region]:e,token:e});this.accessTokens=r,this.registryApi.setAccessTokens(r),o.writeFileSync(n,JSON.stringify(r,null,2))}))}logout(){const e=i.resolve(a.homedir(),p);o.existsSync(e)&&o.unlinkSync(e)}},t.isRedoclyRegistryURL=function(e){const t=c.env.REDOCLY_DOMAIN||c.DOMAINS[c.DEFAULT_REGION],n="redocly.com"===t?"redoc.ly":t;return!(!e.startsWith(`https://api.${t}/registry/`)&&!e.startsWith(`https://api.${n}/registry/`))}},1390:function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,i){function a(e){try{l(r.next(e))}catch(e){i(e)}}function s(e){try{l(r.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}l((r=r.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.RegistryApi=void 0;const o=n(8150),i=n(3777),a=n(771),s=n(3244).i8;t.RegistryApi=class{constructor(e,t){this.accessTokens=e,this.region=t}get accessToken(){return a.isNotEmptyObject(this.accessTokens)&&this.accessTokens[this.region]}getBaseUrl(e=i.DEFAULT_REGION){return`https://api.${i.DOMAINS[e]}/registry`}setAccessTokens(e){return this.accessTokens=e,this}request(e="",t={},n){return r(this,void 0,void 0,(function*(){const r=Object.assign({},t.headers||{},{"x-redocly-cli-version":s});if(!r.hasOwnProperty("authorization"))throw new Error("Unauthorized");const i=yield o.default(`${this.getBaseUrl(n)}${e}`,Object.assign({},t,{headers:r}));if(401===i.status)throw new Error("Unauthorized");if(404===i.status){const e=yield i.json();throw new Error(e.code)}return i}))}authStatus(e,t,n=!1){return r(this,void 0,void 0,(function*(){try{const n=yield this.request("",{headers:{authorization:e}},t);return yield n.json()}catch(e){throw n&&console.log(e),e}}))}prepareFileUpload({organizationId:e,name:t,version:n,filesHash:o,filename:i,isUpsert:a}){return r(this,void 0,void 0,(function*(){const r=yield this.request(`/${e}/${t}/${n}/prepare-file-upload`,{method:"POST",headers:{"content-type":"application/json",authorization:this.accessToken},body:JSON.stringify({filesHash:o,filename:i,isUpsert:a})},this.region);if(r.ok)return r.json();throw new Error("Could not prepare file upload")}))}pushApi({organizationId:e,name:t,version:n,rootFilePath:o,filePaths:i,branch:a,isUpsert:s,isPublic:l,batchId:c,batchSize:u}){return r(this,void 0,void 0,(function*(){if(!(yield this.request(`/${e}/${t}/${n}`,{method:"PUT",headers:{"content-type":"application/json",authorization:this.accessToken},body:JSON.stringify({rootFilePath:o,filePaths:i,branch:a,isUpsert:s,isPublic:l,batchId:c,batchSize:u})},this.region)).ok)throw new Error("Could not push api")}))}}},7468:function(e,t){"use strict";function n(e,t){return""===e&&(e="#/"),"/"===e[e.length-1]?e+t:e+"/"+t}Object.defineProperty(t,"__esModule",{value:!0}),t.isMappingRef=t.isAbsoluteUrl=t.refBaseName=t.pointerBaseName=t.parsePointer=t.parseRef=t.escapePointer=t.unescapePointer=t.Location=t.isRef=t.joinPointer=void 0,t.joinPointer=n,t.isRef=function(e){return e&&"string"==typeof e.$ref};class r{constructor(e,t){this.source=e,this.pointer=t}child(e){return new r(this.source,n(this.pointer,(Array.isArray(e)?e:[e]).map(i).join("/")))}key(){return Object.assign(Object.assign({},this),{reportOnKey:!0})}get absolutePointer(){return this.source.absoluteRef+("#/"===this.pointer?"":this.pointer)}}function o(e){return decodeURIComponent(e.replace(/~1/g,"/").replace(/~0/g,"~"))}function i(e){return"number"==typeof e?e:e.replace(/~/g,"~0").replace(/\//g,"~1")}t.Location=r,t.unescapePointer=o,t.escapePointer=i,t.parseRef=function(e){const[t,n]=e.split("#/");return{uri:t||null,pointer:n?n.split("/").map(o).filter(Boolean):[]}},t.parsePointer=function(e){return e.substr(2).split("/").map(o)},t.pointerBaseName=function(e){const t=e.split("/");return t[t.length-1]},t.refBaseName=function(e){const t=e.split(/[\/\\]/);return t[t.length-1].replace(/\.[^.]+$/,"")},t.isAbsoluteUrl=function(e){return e.startsWith("http://")||e.startsWith("https://")},t.isMappingRef=function(e){return e.startsWith("#")||e.startsWith("https://")||e.startsWith("http://")||e.startsWith("./")||e.startsWith("../")||e.indexOf("/")>-1}},4182:function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,i){function a(e){try{l(r.next(e))}catch(e){i(e)}}function s(e){try{l(r.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}l((r=r.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.resolveDocument=t.BaseResolver=t.makeDocumentFromString=t.makeRefId=t.YamlParseError=t.ResolveError=t.Source=void 0;const o=n(3197),i=n(6470),a=n(7468),s=n(5220),l=n(771);class c{constructor(e,t,n){this.absoluteRef=e,this.body=t,this.mimeType=n}getAst(e){var t;return void 0===this._ast&&(this._ast=null!==(t=e(this.body,{filename:this.absoluteRef}))&&void 0!==t?t:void 0,this._ast&&0===this._ast.kind&&""===this._ast.value&&1!==this._ast.startPosition&&(this._ast.startPosition=1,this._ast.endPosition=1)),this._ast}getLines(){return void 0===this._lines&&(this._lines=this.body.split(/\r\n|[\n\r]/g)),this._lines}}t.Source=c;class u extends Error{constructor(e){super(e.message),this.originalError=e,Object.setPrototypeOf(this,u.prototype)}}t.ResolveError=u;const p=/\((\d+):(\d+)\)$/;class d extends Error{constructor(e,t){super(e.message.split("\n")[0]),this.originalError=e,this.source=t,Object.setPrototypeOf(this,d.prototype);const[,n,r]=this.message.match(p)||[];this.line=parseInt(n,10),this.col=parseInt(r,10)}}function f(e,t){return e+"::"+t}function h(e,t){return{prev:e,node:t}}t.YamlParseError=d,t.makeRefId=f,t.makeDocumentFromString=function(e,t){const n=new c(t,e);try{return{source:n,parsed:l.parseYaml(e,{filename:t})}}catch(e){throw new d(e,n)}},t.BaseResolver=class{constructor(e={http:{headers:[]}}){this.config=e,this.cache=new Map}getFiles(){return new Set(Array.from(this.cache.keys()))}resolveExternalRef(e,t){return a.isAbsoluteUrl(t)?t:e&&a.isAbsoluteUrl(e)?new URL(t,e).href:i.resolve(e?i.dirname(e):process.cwd(),t)}loadExternalRef(e){return r(this,void 0,void 0,(function*(){try{if(a.isAbsoluteUrl(e)){const{body:t,mimeType:n}=yield l.readFileFromUrl(e,this.config.http);return new c(e,t,n)}return new c(e,yield o.promises.readFile(e,"utf-8"))}catch(e){throw new u(e)}}))}parseDocument(e,t=!1){var n;const r=e.absoluteRef.substr(e.absoluteRef.lastIndexOf("."));if(![".json",".json",".yml",".yaml"].includes(r)&&!(null===(n=e.mimeType)||void 0===n?void 0:n.match(/(json|yaml|openapi)/))&&!t)return{source:e,parsed:e.body};try{return{source:e,parsed:l.parseYaml(e.body,{filename:e.absoluteRef})}}catch(t){throw new d(t,e)}}resolveDocument(e,t,n=!1){return r(this,void 0,void 0,(function*(){const r=this.resolveExternalRef(e,t),o=this.cache.get(r);if(o)return o;const i=this.loadExternalRef(r).then((e=>this.parseDocument(e,n)));return this.cache.set(r,i),i}))}};const m={name:"unknown",properties:{}},g={name:"scalar",properties:{}};t.resolveDocument=function(e){return r(this,void 0,void 0,(function*(){const{rootDocument:t,externalRefResolver:n,rootType:o}=e,i=new Map,l=new Set,c=[];let u;!function e(t,o,u,p){function d(e,t,o){return r(this,void 0,void 0,(function*(){if(function(e,t){for(;e;){if(e.node===t)return!0;e=e.prev}return!1}(o.prev,t))throw new Error("Self-referencing circular pointer");const{uri:r,pointer:s}=a.parseRef(t.$ref),l=null!==r;let c;try{c=l?yield n.resolveDocument(e.source.absoluteRef,r):e}catch(n){const r={resolved:!1,isRemote:l,document:void 0,error:n},o=f(e.source.absoluteRef,t.$ref);return i.set(o,r),r}let u={resolved:!0,document:c,isRemote:l,node:e.parsed,nodePointer:"#/"},p=c.parsed;const m=s;for(let e of m){if("object"!=typeof p){p=void 0;break}if(void 0!==p[e])p=p[e],u.nodePointer=a.joinPointer(u.nodePointer,a.escapePointer(e));else{if(!a.isRef(p)){p=void 0;break}if(u=yield d(c,p,h(o,p)),c=u.document||c,"object"!=typeof u.node){p=void 0;break}p=u.node[e],u.nodePointer=a.joinPointer(u.nodePointer,a.escapePointer(e))}}u.node=p,u.document=c;const g=f(e.source.absoluteRef,t.$ref);return u.document&&a.isRef(p)&&(u=yield d(u.document,p,h(o,p))),i.set(g,u),Object.assign({},u)}))}!function t(n,r,i){if("object"!=typeof n||null===n)return;const u=`${r.name}::${i}`;if(!l.has(u))if(l.add(u),Array.isArray(n)){const e=r.items;if(r!==m&&void 0===e)return;for(let r=0;r{t.resolved&&e(t.node,t.document,t.nodePointer,r)}));c.push(t)}}}(t,p,o.source.absoluteRef+u)}(t.parsed,t,"#/",o);do{u=yield Promise.all(c)}while(c.length!==u.length);return i}))}},7275:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateJsonSchema=t.releaseAjvInstance=void 0;const r=n(5499),o=n(7468);let i=null;t.releaseAjvInstance=function(){i=null},t.validateJsonSchema=function(e,t,n,a,s,l){const c=function(e,t,n,o){const a=function(e,t){return i||(i=new r.default({schemaId:"$id",meta:!0,allErrors:!0,strictSchema:!1,inlineRefs:!1,validateSchema:!1,discriminator:!0,allowUnionTypes:!0,validateFormats:!1,defaultAdditionalProperties:!t,loadSchemaSync(t,n){const r=e({$ref:n},t.split("#")[0]);return!(!r||!r.location)&&Object.assign({$id:r.location.absolutePointer},r.node)},logger:!1})),i}(n,o);return a.getSchema(t.absolutePointer)||a.addSchema(Object.assign({$id:t.absolutePointer},e),t.absolutePointer),a.getSchema(t.absolutePointer)}(t,n,s,l);return c?{valid:!!c(e,{instancePath:a,parentData:{fake:{}},parentDataProperty:"fake",rootData:{},dynamicAnchors:{}}),errors:(c.errors||[]).map((function(e){let t=e.message,n="enum"===e.keyword?e.params.allowedValues:void 0;n&&(t+=` ${n.map((e=>`"${e}"`)).join(", ")}`),"type"===e.keyword&&(t=`type ${t}`);const r=e.instancePath.substring(a.length+1),i=r.substring(r.lastIndexOf("/")+1);if(i&&(t=`\`${i}\` property ${t}`),"additionalProperties"===e.keyword){const n=e.params.additionalProperty;t=`${t} \`${n}\``,e.instancePath+="/"+o.escapePointer(n)}return Object.assign(Object.assign({},e),{message:t,suggest:n})}))}:{valid:!0,errors:[]}}},9740:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.asserts=t.runOnValuesSet=t.runOnKeysSet=void 0;const r=n(771),o=n(5738);t.runOnKeysSet=new Set(["mutuallyExclusive","mutuallyRequired","enum","pattern","minLength","maxLength","casing","sortOrder","disallowed","required","requireAny","ref"]),t.runOnValuesSet=new Set(["pattern","enum","defined","undefined","nonEmpty","minLength","maxLength","casing","sortOrder","ref"]),t.asserts={pattern:(e,t,n)=>{if(void 0===e)return{isValid:!0};const i=r.isString(e)?[e]:e,a=o.regexFromString(t);for(let t of i)if(!(null==a?void 0:a.test(t)))return{isValid:!1,location:r.isString(e)?n:n.key()};return{isValid:!0}},enum:(e,t,n)=>{if(void 0===e)return{isValid:!0};const o=r.isString(e)?[e]:e;for(let i of o)if(!t.includes(i))return{isValid:!1,location:r.isString(e)?n:n.child(i).key()};return{isValid:!0}},defined:(e,t=!0,n)=>{const r=void 0!==e;return{isValid:t?r:!r,location:n}},required:(e,t,n)=>{for(const r of t)if(!e.includes(r))return{isValid:!1,location:n.key()};return{isValid:!0}},disallowed:(e,t,n)=>{if(void 0===e)return{isValid:!0};const o=r.isString(e)?[e]:e;for(let i of o)if(t.includes(i))return{isValid:!1,location:r.isString(e)?n:n.child(i).key()};return{isValid:!0}},undefined:(e,t=!0,n)=>{const r=void 0===e;return{isValid:t?r:!r,location:n}},nonEmpty:(e,t=!0,n)=>{const r=null==e||""===e;return{isValid:t?!r:r,location:n}},minLength:(e,t,n)=>void 0===e?{isValid:!0}:{isValid:e.length>=t,location:n},maxLength:(e,t,n)=>void 0===e?{isValid:!0}:{isValid:e.length<=t,location:n},casing:(e,t,n)=>{if(void 0===e)return{isValid:!0};const o=r.isString(e)?[e]:e;for(let i of o){let o=!1;switch(t){case"camelCase":o=!!i.match(/^[a-z][a-zA-Z0-9]+$/g);break;case"kebab-case":o=!!i.match(/^([a-z][a-z0-9]*)(-[a-z0-9]+)*$/g);break;case"snake_case":o=!!i.match(/^([a-z][a-z0-9]*)(_[a-z0-9]+)*$/g);break;case"PascalCase":o=!!i.match(/^[A-Z][a-zA-Z0-9]+$/g);break;case"MACRO_CASE":o=!!i.match(/^([A-Z][A-Z0-9]*)(_[A-Z0-9]+)*$/g);break;case"COBOL-CASE":o=!!i.match(/^([A-Z][A-Z0-9]*)(-[A-Z0-9]+)*$/g);break;case"flatcase":o=!!i.match(/^[a-z][a-z0-9]+$/g)}if(!o)return{isValid:!1,location:r.isString(e)?n:n.child(i).key()}}return{isValid:!0}},sortOrder:(e,t,n)=>void 0===e?{isValid:!0}:{isValid:o.isOrdered(e,t),location:n},mutuallyExclusive:(e,t,n)=>({isValid:o.getIntersectionLength(e,t)<2,location:n.key()}),mutuallyRequired:(e,t,n)=>({isValid:!(o.getIntersectionLength(e,t)>0)||o.getIntersectionLength(e,t)===t.length,location:n.key()}),requireAny:(e,t,n)=>({isValid:o.getIntersectionLength(e,t)>=1,location:n.key()}),ref:(e,t,n,r)=>{if(void 0===r)return{isValid:!0};const i=r.hasOwnProperty("$ref");if("boolean"==typeof t)return{isValid:t?i:!i,location:i?n:n.key()};const a=o.regexFromString(t);return{isValid:i&&(null==a?void 0:a.test(r.$ref)),location:i?n:n.key()}}}},4015:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Assertions=void 0;const r=n(9740),o=n(5738);t.Assertions=e=>{let t=[];const n=Object.values(e).filter((e=>"object"==typeof e&&null!==e));for(const[e,i]of n.entries()){const n=i.assertionId&&`${i.assertionId} assertion`||`assertion #${e+1}`;if(!i.subject)throw new Error(`${n}: 'subject' is required`);const a=Array.isArray(i.subject)?i.subject:[i.subject],s=Object.keys(r.asserts).filter((e=>void 0!==i[e])).map((e=>({assertId:n,name:e,conditions:i[e],message:i.message,severity:i.severity||"error",suggest:i.suggest||[],runsOnKeys:r.runOnKeysSet.has(e),runsOnValues:r.runOnValuesSet.has(e)}))),l=s.find((e=>e.runsOnKeys&&!e.runsOnValues)),c=s.find((e=>e.runsOnValues&&!e.runsOnKeys));if(c&&!i.property)throw new Error(`${c.name} can't be used on all keys. Please provide a single property.`);if(l&&i.property)throw new Error(`${l.name} can't be used on a single property. Please use 'property'.`);for(const e of a){const n=o.buildSubjectVisitor(i.property,s,i.context),r=o.buildVisitorObject(e,i.context,n);t.push(r)}}return t}},5738:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.regexFromString=t.isOrdered=t.getIntersectionLength=t.buildSubjectVisitor=t.buildVisitorObject=void 0;const r=n(7468),o=n(9740);function i({values:e,rawValues:t,assert:n,location:r,report:i}){const a=o.asserts[n.name](e,n.conditions,r,t);a.isValid||i({message:n.message||`The ${n.assertId} doesn't meet required conditions`,location:a.location||r,forceSeverity:n.severity,suggest:n.suggest,ruleId:n.assertId})}t.buildVisitorObject=function(e,t,n){if(!t)return{[e]:n};let r={};const o=r;for(let n=0;ni?!i.includes(t):a?a.includes(t):void 0}:{},r=r[o.type]}return r[e]=n,o},t.buildSubjectVisitor=function(e,t,n){return(o,{report:a,location:s,rawLocation:l,key:c,type:u,resolve:p,rawNode:d})=>{var f;if(n){const e=n[n.length-1];if(e.type===u.name){const t=e.matchParentKeys,n=e.excludeParentKeys;if(t&&!t.includes(c))return;if(n&&n.includes(c))return}}e&&(e=Array.isArray(e)?e:[e]);for(const n of t){const t="ref"===n.name?l:s;if(e)for(const s of e)i({values:r.isRef(o[s])?null===(f=p(o[s]))||void 0===f?void 0:f.node:o[s],rawValues:d[s],assert:n,location:t.child(s),report:a});else{const e="ref"===n.name?d:Object.keys(o);i({values:Object.keys(o),rawValues:e,assert:n,location:t,report:a})}}}},t.getIntersectionLength=function(e,t){const n=new Set(t);let r=0;for(const t of e)n.has(t)&&r++;return r},t.isOrdered=function(e,t){const n=t.direction||t,r=t.property;for(let t=1;t=i:o<=i))return!1}return!0},t.regexFromString=function(e){const t=e.match(/^\/(.*)\/(.*)|(.*)/);return t&&new RegExp(t[1]||t[3],t[2])}},8265:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.InfoContact=void 0;const r=n(780);t.InfoContact=()=>({Info(e,{report:t,location:n}){e.contact||t({message:r.missingRequiredField("Info","contact"),location:n.child("contact").key()})}})},8675:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.InfoDescription=void 0;const r=n(780);t.InfoDescription=()=>({Info(e,t){r.validateDefinedAndNonEmpty("description",e,t)}})},9622:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.InfoLicense=void 0;const r=n(780);t.InfoLicense=()=>({Info(e,{report:t}){e.license||t({message:r.missingRequiredField("Info","license"),location:{reportOnKey:!0}})}})},476:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.InfoLicenseUrl=void 0;const r=n(780);t.InfoLicenseUrl=()=>({License(e,t){r.validateDefinedAndNonEmpty("url",e,t)}})},3467:function(e,t){"use strict";function n(e,t){const n=e.split("/"),r=t.split("/");if(n.length!==r.length)return!1;let o=0,i=0,a=!0;for(let e=0;e({PathMap(e,{report:t,location:r}){const o=[];for(const i of Object.keys(e)){const e=o.find((e=>n(e,i)));e&&t({message:`Paths should resolve unambiguously. Found two ambiguous paths: \`${e}\` and \`${i}\`.`,location:r.child([i]).key()}),o.push(i)}}})},2319:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoEnumTypeMismatch=void 0;const r=n(780);t.NoEnumTypeMismatch=()=>({Schema(e,{report:t,location:n}){if(!e.enum||Array.isArray(e.enum)){if(e.enum&&e.type&&!Array.isArray(e.type)){const o=e.enum.filter((t=>!r.matchesJsonSchemaType(t,e.type,e.nullable)));for(const i of o)t({message:`All values of \`enum\` field must be of the same type as the \`type\` field: expected "${e.type}" but received "${r.oasTypeOf(i)}".`,location:n.child(["enum",e.enum.indexOf(i)])})}if(e.enum&&e.type&&Array.isArray(e.type)){const o={};for(const t of e.enum){o[t]=[];for(const n of e.type)r.matchesJsonSchemaType(t,n,e.nullable)||o[t].push(n);o[t].length!==e.type.length&&delete o[t]}for(const r of Object.keys(o))t({message:`Enum value \`${r}\` must be of one type. Allowed types: \`${e.type}\`.`,location:n.child(["enum",e.enum.indexOf(r)])})}}}})},525:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoHttpVerbsInPaths=void 0;const r=n(771),o=["get","head","post","put","patch","delete","options","trace"];t.NoHttpVerbsInPaths=({splitIntoWords:e})=>({PathItem(t,{key:n,report:i,location:a}){const s=n.toString();if(!s.startsWith("/"))return;const l=s.split("/");for(const t of l){if(!t||r.isPathParameter(t))continue;const n=n=>e?r.splitCamelCaseIntoWords(t).has(n):t.toLocaleLowerCase().includes(n);for(const e of o)n(e)&&i({message:`path \`${s}\` should not contain http verb ${e}`,location:a.key()})}}})},4628:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoIdenticalPaths=void 0,t.NoIdenticalPaths=()=>({PathMap(e,{report:t,location:n}){const r=new Map;for(const o of Object.keys(e)){const e=o.replace(/{.+?}/g,"{VARIABLE}"),i=r.get(e);i?t({message:`The path already exists which differs only by path parameter name(s): \`${i}\` and \`${o}\`.`,location:n.child([o]).key()}):r.set(e,o)}}})},1562:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoInvalidParameterExamples=void 0;const r=n(780);t.NoInvalidParameterExamples=e=>{var t;const n=null===(t=e.disallowAdditionalProperties)||void 0===t||t;return{Parameter:{leave(e,t){if(e.example&&r.validateExample(e.example,e.schema,t.location.child("example"),t,n),e.examples)for(const[n,o]of Object.entries(e.examples))"value"in o&&r.validateExample(o.value,e.schema,t.location.child(["examples",n]),t,!1)}}}}},78:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoInvalidSchemaExamples=void 0;const r=n(780);t.NoInvalidSchemaExamples=e=>{var t;const n=null===(t=e.disallowAdditionalProperties)||void 0===t||t;return{Schema:{leave(e,t){if(e.examples)for(const o of e.examples)r.validateExample(o,e,t.location.child(["examples",e.examples.indexOf(o)]),t,n);e.example&&r.validateExample(e.example,e,t.location.child("example"),t,!1)}}}}},700:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoPathTrailingSlash=void 0,t.NoPathTrailingSlash=()=>({PathItem(e,{report:t,key:n,location:r}){n.endsWith("/")&&"/"!==n&&t({message:`\`${n}\` should not have a trailing slash.`,location:r.key()})}})},5946:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Operation2xxResponse=void 0,t.Operation2xxResponse=()=>({ResponsesMap(e,{report:t}){Object.keys(e).some((e=>"default"===e||/2[Xx0-9]{2}/.test(e)))||t({message:"Operation must have at least one `2xx` response.",location:{reportOnKey:!0}})}})},5281:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Operation4xxResponse=void 0,t.Operation4xxResponse=()=>({ResponsesMap(e,{report:t}){Object.keys(e).some((e=>/4[Xx0-9]{2}/.test(e)))||t({message:"Operation must have at least one `4xx` response.",location:{reportOnKey:!0}})}})},3408:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationDescription=void 0;const r=n(780);t.OperationDescription=()=>({Operation(e,t){r.validateDefinedAndNonEmpty("description",e,t)}})},8742:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationIdUnique=void 0,t.OperationIdUnique=()=>{const e=new Set;return{Operation(t,{report:n,location:r}){t.operationId&&(e.has(t.operationId)&&n({message:"Every operation must have a unique `operationId`.",location:r.child([t.operationId])}),e.add(t.operationId))}}}},5064:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationIdUrlSafe=void 0;const n=/^[A-Za-z0-9-._~:/?#\[\]@!\$&'()*+,;=]*$/;t.OperationIdUrlSafe=()=>({Operation(e,{report:t,location:r}){e.operationId&&!n.test(e.operationId)&&t({message:"Operation `operationId` should not have URL invalid characters.",location:r.child(["operationId"])})}})},8786:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationOperationId=void 0;const r=n(780);t.OperationOperationId=()=>({DefinitionRoot:{PathItem:{Operation(e,t){r.validateDefinedAndNonEmpty("operationId",e,t)}}}})},4112:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationParametersUnique=void 0,t.OperationParametersUnique=()=>{let e,t;return{PathItem:{enter(){e=new Set},Parameter(t,{report:n,key:r,parentLocations:o}){const i=`${t.in}___${t.name}`;e.has(i)&&n({message:`Paths must have unique \`name\` + \`in\` parameters.\nRepeats of \`in:${t.in}\` + \`name:${t.name}\`.`,location:o.PathItem.child(["parameters",r])}),e.add(`${t.in}___${t.name}`)},Operation:{enter(){t=new Set},Parameter(e,{report:n,key:r,parentLocations:o}){const i=`${e.in}___${e.name}`;t.has(i)&&n({message:`Operations must have unique \`name\` + \`in\` parameters. Repeats of \`in:${e.in}\` + \`name:${e.name}\`.`,location:o.Operation.child(["parameters",r])}),t.add(i)}}}}}},7892:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationSecurityDefined=void 0,t.OperationSecurityDefined=()=>{let e=new Map;return{DefinitionRoot:{leave(t,{report:n}){for(const[t,r]of e.entries())if(!r.defined)for(const e of r.from)n({message:`There is no \`${t}\` security scheme defined.`,location:e.key()})}},SecurityScheme(t,{key:n}){e.set(n.toString(),{defined:!0,from:[]})},SecurityRequirement(t,{location:n}){for(const r of Object.keys(t)){const t=e.get(r),o=n.child([r]);t?t.from.push(o):e.set(r,{from:[o]})}}}}},8613:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationSingularTag=void 0,t.OperationSingularTag=()=>({Operation(e,{report:t,location:n}){e.tags&&e.tags.length>1&&t({message:"Operation `tags` object should have only one tag.",location:n.child(["tags"]).key()})}})},9578:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationSummary=void 0;const r=n(780);t.OperationSummary=()=>({Operation(e,t){r.validateDefinedAndNonEmpty("summary",e,t)}})},5097:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationTagDefined=void 0,t.OperationTagDefined=()=>{let e;return{DefinitionRoot(t){var n;e=new Set((null!==(n=t.tags)&&void 0!==n?n:[]).map((e=>e.name)))},Operation(t,{report:n,location:r}){if(t.tags)for(let o=0;o({Parameter(e,{report:t,location:n}){void 0===e.description?t({message:"Parameter object description must be present.",location:{reportOnKey:!0}}):e.description||t({message:"Parameter object description must be non-empty string.",location:n.child(["description"])})}})},7890:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathDeclarationMustExist=void 0,t.PathDeclarationMustExist=()=>({PathItem(e,{report:t,key:n}){-1!==n.indexOf("{}")&&t({message:"Path parameter declarations must be non-empty. `{}` is invalid.",location:{reportOnKey:!0}})}})},3689:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathExcludesPatterns=void 0,t.PathExcludesPatterns=({patterns:e})=>({PathItem(t,{report:n,key:r,location:o}){if(!e)throw new Error('Parameter "patterns" is not provided for "path-excludes-patterns" rule');const i=r.toString();if(i.startsWith("/")){const t=e.filter((e=>i.match(e)));for(const e of t)n({message:`path \`${i}\` should not match regex pattern: \`${e}\``,location:o.key()})}}})},2332:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathHttpVerbsOrder=void 0;const n=["get","head","post","put","patch","delete","options","trace"];t.PathHttpVerbsOrder=e=>{const t=e&&e.order||n;if(!Array.isArray(t))throw new Error("path-http-verbs-order `order` option must be an array");return{PathItem(e,{report:n,location:r}){const o=Object.keys(e).filter((e=>t.includes(e)));for(let e=0;e({PathMap:{PathItem(e,{report:t,key:n}){n.toString().includes("?")&&t({message:"Don't put query string items in the path, they belong in parameters with `in: query`.",location:{reportOnKey:!0}})}}})},7421:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathParamsDefined=void 0;const n=/\{([a-zA-Z0-9_.-]+)\}+/g;t.PathParamsDefined=()=>{let e,t,r;return{PathItem:{enter(o,{key:i}){t=new Set,r=i,e=new Set(Array.from(i.toString().matchAll(n)).map((e=>e[1])))},Parameter(n,{report:o,location:i}){"path"===n.in&&n.name&&(t.add(n.name),e.has(n.name)||o({message:`Path parameter \`${n.name}\` is not used in the path \`${r}\`.`,location:i.child(["name"])}))},Operation:{leave(n,{report:o,location:i}){for(const n of Array.from(e.keys()))t.has(n)||o({message:`The operation does not define the path parameter \`{${n}}\` expected by path \`${r}\`.`,location:i.child(["parameters"]).key()})},Parameter(n,{report:o,location:i}){"path"===n.in&&n.name&&(t.add(n.name),e.has(n.name)||o({message:`Path parameter \`${n.name}\` is not used in the path \`${r}\`.`,location:i.child(["name"])}))}}}}}},3807:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathSegmentPlural=void 0;const r=n(771);t.PathSegmentPlural=e=>{const{ignoreLastPathSegment:t,exceptions:n}=e;return{PathItem:{leave(e,{report:o,key:i,location:a}){const s=i.toString();if(s.startsWith("/")){const e=s.split("/");e.shift(),t&&e.length>1&&e.pop();for(const t of e)n&&n.includes(t)||!r.isPathParameter(t)&&r.isSingular(t)&&o({message:`path segment \`${t}\` should be plural.`,location:a.key()})}}}}}},9527:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathsKebabCase=void 0,t.PathsKebabCase=()=>({PathItem(e,{report:t,key:n}){n.substr(1).split("/").filter((e=>""!==e)).every((e=>/^{.+}$/.test(e)||/^[a-z0-9-.]+$/.test(e)))||t({message:`\`${n}\` does not use kebab-case.`,location:{reportOnKey:!0}})}})},5839:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ResponseContainsHeader=void 0;const r=n(771);t.ResponseContainsHeader=e=>{const t=e.names||{};return{Operation:{Response:{enter:(e,{report:n,location:o,key:i})=>{var a;const s=t[i]||t[r.getMatchingStatusCodeRange(i)]||t[r.getMatchingStatusCodeRange(i).toLowerCase()]||[];for(const t of s)(null===(a=e.headers)||void 0===a?void 0:a[t])||n({message:`Response object must contain a "${t}" header.`,location:o.child("headers").key()})}}}}}},5669:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ScalarPropertyMissingExample=void 0;const r=n(1510),o=["string","integer","number","boolean","null"];t.ScalarPropertyMissingExample=()=>({SchemaProperties(e,{report:t,location:n,oasVersion:i,resolve:a}){for(const l of Object.keys(e)){const c=a(e[l]).node;c&&((s=c).type&&!(s.allOf||s.anyOf||s.oneOf)&&"binary"!==s.format&&(Array.isArray(s.type)?s.type.every((e=>o.includes(e))):o.includes(s.type)))&&void 0===c.example&&void 0===c.examples&&t({message:`Scalar property should have "example"${i===r.OasVersion.Version3_1?' or "examples"':""} defined.`,location:n.child(l).key()})}var s}})},6471:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OasSpec=void 0;const r=n(5220),o=n(780),i=n(7468),a=n(771);t.OasSpec=()=>({any(e,{report:t,type:n,location:s,key:l,resolve:c,ignoreNextVisitorsOnNode:u}){var p,d,f,h;const m=o.oasTypeOf(e);if(n.items)return void("array"!==m&&(t({message:`Expected type \`${n.name}\` (array) but got \`${m}\``}),u()));if("object"!==m)return t({message:`Expected type \`${n.name}\` (object) but got \`${m}\``}),void u();const g="function"==typeof n.required?n.required(e,l):n.required;for(let n of g||[])e.hasOwnProperty(n)||t({message:`The field \`${n}\` must be present on this level.`,location:[{reportOnKey:!0}]});const y=null===(p=n.allowed)||void 0===p?void 0:p.call(n,e);if(y&&a.isPlainObject(e))for(const r in e)y.includes(r)||n.extensionsPrefix&&r.startsWith(n.extensionsPrefix)||!Object.keys(n.properties).includes(r)||t({message:`The field \`${r}\` is not allowed here.`,location:s.child([r]).key()});const v=n.requiredOneOf||null;if(v){let r=!1;for(let t of v||[])e.hasOwnProperty(t)&&(r=!0);r||t({message:`Must contain at least one of the following fields: ${null===(d=n.requiredOneOf)||void 0===d?void 0:d.join(", ")}.`,location:[{reportOnKey:!0}]})}for(const a of Object.keys(e)){const l=s.child([a]);let u=e[a],p=n.properties[a];if(void 0===p&&(p=n.additionalProperties),"function"==typeof p&&(p=p(u,a)),r.isNamedType(p))continue;const d=p,m=o.oasTypeOf(u);if(void 0!==d){if(null!==d){if(!1!==d.resolvable&&i.isRef(u)&&(u=c(u).node),d.enum)d.enum.includes(u)||t({location:l,message:`\`${a}\` can be one of the following only: ${d.enum.map((e=>`"${e}"`)).join(", ")}.`,suggest:o.getSuggest(u,d.enum)});else if(d.type&&!o.matchesJsonSchemaType(u,d.type,!1))t({message:`Expected type \`${d.type}\` but got \`${m}\`.`,location:l});else if("array"===m&&(null===(f=d.items)||void 0===f?void 0:f.type)){const e=null===(h=d.items)||void 0===h?void 0:h.type;for(let n=0;ne[a]&&t({message:`The value of the ${a} field must be greater than or equal to ${d.minimum}`,location:s.child([a])})}}else{if(a.startsWith("x-"))continue;t({message:`Property \`${a}\` is not expected here.`,suggest:o.getSuggest(a,Object.keys(n.properties)),location:l.key()})}}}})},7281:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.TagDescription=void 0;const r=n(780);t.TagDescription=()=>({Tag(e,t){r.validateDefinedAndNonEmpty("description",e,t)}})},6855:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.TagsAlphabetical=void 0,t.TagsAlphabetical=()=>({DefinitionRoot(e,{report:t,location:n}){if(e.tags)for(let r=0;re.tags[r+1].name&&t({message:"The `tags` array should be in alphabetical order.",location:n.child(["tags",r])})}})},348:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.reportUnresolvedRef=t.NoUnresolvedRefs=void 0;const r=n(4182);function o(e,t,n){var o;const i=e.error;i instanceof r.YamlParseError&&t({message:"Failed to parse: "+i.message,location:{source:i.source,pointer:void 0,start:{col:i.col,line:i.line}}});const a=null===(o=e.error)||void 0===o?void 0:o.message;t({location:n,message:"Can't resolve $ref"+(a?": "+a:"")})}t.NoUnresolvedRefs=()=>({ref:{leave(e,{report:t,location:n},r){void 0===r.node&&o(r,t,n)}},DiscriminatorMapping(e,{report:t,resolve:n,location:r}){for(const i of Object.keys(e)){const a=n({$ref:e[i]});if(void 0!==a.node)return;o(a,t,r.child(i))}}}),t.reportUnresolvedRef=o},9566:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.BooleanParameterPrefixes=void 0,t.BooleanParameterPrefixes=e=>{const t=e.prefixes||["is","has"],n=new RegExp(`^(${t.join("|")})[A-Z-_]`),r=t.map((e=>`\`${e}\``)),o=1===r.length?r[0]:r.slice(0,-1).join(", ")+" or "+r[t.length-1];return{Parameter(e,{report:t,location:r}){"boolean"!==e.type||n.test(e.name)||t({message:`Boolean parameter \`${e.name}\` should have ${o} prefix.`,location:r.child("name")})}}}},7523:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.preprocessors=t.rules=void 0;const r=n(6471),o=n(78),i=n(1562),a=n(8675),s=n(8265),l=n(9622),c=n(476),u=n(9566),p=n(7281),d=n(6855),f=n(9527),h=n(2319),m=n(700),g=n(5946),y=n(5281),v=n(4015),b=n(8742),w=n(4112),x=n(7421),k=n(5097),_=n(7890),O=n(5064),S=n(3408),E=n(5023),P=n(3529),A=n(8613),$=n(7892),C=n(348),R=n(2332),j=n(4628),T=n(8786),I=n(9578),N=n(3467),D=n(525),L=n(3689),M=n(7028),F=n(1750),z=n(3807),U=n(5839),V=n(7899),B=n(5669);t.rules={spec:r.OasSpec,"no-invalid-schema-examples":o.NoInvalidSchemaExamples,"no-invalid-parameter-examples":i.NoInvalidParameterExamples,"info-description":a.InfoDescription,"info-contact":s.InfoContact,"info-license":l.InfoLicense,"info-license-url":c.InfoLicenseUrl,"tag-description":p.TagDescription,"tags-alphabetical":d.TagsAlphabetical,"paths-kebab-case":f.PathsKebabCase,"no-enum-type-mismatch":h.NoEnumTypeMismatch,"boolean-parameter-prefixes":u.BooleanParameterPrefixes,"no-path-trailing-slash":m.NoPathTrailingSlash,"operation-2xx-response":g.Operation2xxResponse,"operation-4xx-response":y.Operation4xxResponse,assertions:v.Assertions,"operation-operationId-unique":b.OperationIdUnique,"operation-parameters-unique":w.OperationParametersUnique,"path-parameters-defined":x.PathParamsDefined,"operation-tag-defined":k.OperationTagDefined,"path-declaration-must-exist":_.PathDeclarationMustExist,"operation-operationId-url-safe":O.OperationIdUrlSafe,"operation-operationId":T.OperationOperationId,"operation-summary":I.OperationSummary,"operation-description":S.OperationDescription,"path-not-include-query":E.PathNotIncludeQuery,"path-params-defined":x.PathParamsDefined,"parameter-description":P.ParameterDescription,"operation-singular-tag":A.OperationSingularTag,"operation-security-defined":$.OperationSecurityDefined,"no-unresolved-refs":C.NoUnresolvedRefs,"no-identical-paths":j.NoIdenticalPaths,"no-ambiguous-paths":N.NoAmbiguousPaths,"path-http-verbs-order":R.PathHttpVerbsOrder,"no-http-verbs-in-paths":D.NoHttpVerbsInPaths,"path-excludes-patterns":L.PathExcludesPatterns,"request-mime-type":M.RequestMimeType,"response-mime-type":F.ResponseMimeType,"path-segment-plural":z.PathSegmentPlural,"response-contains-header":U.ResponseContainsHeader,"response-contains-property":V.ResponseContainsProperty,"scalar-property-missing-example":B.ScalarPropertyMissingExample},t.preprocessors={}},4508:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RemoveUnusedComponents=void 0;const r=n(771);t.RemoveUnusedComponents=()=>{let e=new Map;function t(t,n,r){var o;e.set(t.absolutePointer,{used:(null===(o=e.get(t.absolutePointer))||void 0===o?void 0:o.used)||!1,componentType:n,name:r})}return{ref:{leave(t,{type:n,resolve:r,key:o}){if(["Schema","Parameter","Response","SecurityScheme"].includes(n.name)){const n=r(t);if(!n.location)return;e.set(n.location.absolutePointer,{used:!0,name:o.toString()})}}},DefinitionRoot:{leave(t,n){const o=n.getVisitorData();o.removedCount=0;let i=new Set;e.forEach((e=>{const{used:n,name:r,componentType:a}=e;!n&&a&&(i.add(a),delete t[a][r],o.removedCount++)}));for(const e of i)r.isEmptyObject(t[e])&&delete t[e]}},NamedSchemas:{Schema(e,{location:n,key:r}){e.allOf||t(n,"definitions",r.toString())}},NamedParameters:{Parameter(e,{location:n,key:r}){t(n,"parameters",r.toString())}},NamedResponses:{Response(e,{location:n,key:r}){t(n,"responses",r.toString())}},NamedSecuritySchemes:{SecurityScheme(e,{location:n,key:r}){t(n,"securityDefinitions",r.toString())}}}}},7028:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RequestMimeType=void 0;const r=n(771);t.RequestMimeType=({allowedValues:e})=>({DefinitionRoot(t,n){r.validateMimeType({type:"consumes",value:t},n,e)},Operation:{leave(t,n){r.validateMimeType({type:"consumes",value:t},n,e)}}})},7899:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ResponseContainsProperty=void 0;const r=n(771);t.ResponseContainsProperty=e=>{const t=e.names||{};let n;return{Operation:{Response:{skip:(e,t)=>"204"==`${t}`,enter:(e,t)=>{n=t.key},Schema(e,{report:o,location:i}){var a;if("object"!==e.type)return;const s=t[n]||t[r.getMatchingStatusCodeRange(n)]||t[r.getMatchingStatusCodeRange(n).toLowerCase()]||[];for(const t of s)(null===(a=e.properties)||void 0===a?void 0:a[t])||o({message:`Response object must contain a top-level "${t}" property.`,location:i.child("properties").key()})}}}}}},1750:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ResponseMimeType=void 0;const r=n(771);t.ResponseMimeType=({allowedValues:e})=>({DefinitionRoot(t,n){r.validateMimeType({type:"produces",value:t},n,e)},Operation:{leave(t,n){r.validateMimeType({type:"produces",value:t},n,e)}}})},962:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.BooleanParameterPrefixes=void 0,t.BooleanParameterPrefixes=e=>{const t=e.prefixes||["is","has"],n=new RegExp(`^(${t.join("|")})[A-Z-_]`),r=t.map((e=>`\`${e}\``)),o=1===r.length?r[0]:r.slice(0,-1).join(", ")+" or "+r[t.length-1];return{Parameter:{Schema(e,{report:t,parentLocations:r},i){"boolean"!==e.type||n.test(i.Parameter.name)||t({message:`Boolean parameter \`${i.Parameter.name}\` should have ${o} prefix.`,location:r.Parameter.child(["name"])})}}}}},226:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.preprocessors=t.rules=void 0;const r=n(6471),o=n(5946),i=n(5281),a=n(4015),s=n(8742),l=n(4112),c=n(7421),u=n(5097),p=n(1265),d=n(2319),f=n(700),h=n(7890),m=n(5064),g=n(6855),y=n(5486),v=n(2947),b=n(8675),w=n(7281),x=n(8265),k=n(9622),_=n(3408),O=n(897),S=n(5023),E=n(3529),P=n(8613),A=n(476),$=n(7892),C=n(348),R=n(962),j=n(9527),T=n(2332),I=n(7020),N=n(9336),D=n(4628),L=n(6208),M=n(8786),F=n(9578),z=n(3467),U=n(472),V=n(525),B=n(3736),q=n(503),W=n(3807),H=n(3689),Y=n(78),K=n(1562),G=n(5839),Q=n(7557),X=n(5669);t.rules={spec:r.OasSpec,"info-description":b.InfoDescription,"info-contact":x.InfoContact,"info-license":k.InfoLicense,"info-license-url":A.InfoLicenseUrl,"operation-2xx-response":o.Operation2xxResponse,"operation-4xx-response":i.Operation4xxResponse,assertions:a.Assertions,"operation-operationId-unique":s.OperationIdUnique,"operation-parameters-unique":l.OperationParametersUnique,"path-parameters-defined":c.PathParamsDefined,"operation-tag-defined":u.OperationTagDefined,"no-example-value-and-externalValue":p.NoExampleValueAndExternalValue,"no-enum-type-mismatch":d.NoEnumTypeMismatch,"no-path-trailing-slash":f.NoPathTrailingSlash,"no-empty-servers":I.NoEmptyServers,"path-declaration-must-exist":h.PathDeclarationMustExist,"operation-operationId-url-safe":m.OperationIdUrlSafe,"operation-operationId":M.OperationOperationId,"operation-summary":F.OperationSummary,"tags-alphabetical":g.TagsAlphabetical,"no-server-example.com":y.NoServerExample,"no-server-trailing-slash":v.NoServerTrailingSlash,"tag-description":w.TagDescription,"operation-description":_.OperationDescription,"no-unused-components":O.NoUnusedComponents,"path-not-include-query":S.PathNotIncludeQuery,"path-params-defined":c.PathParamsDefined,"parameter-description":E.ParameterDescription,"operation-singular-tag":P.OperationSingularTag,"operation-security-defined":$.OperationSecurityDefined,"no-unresolved-refs":C.NoUnresolvedRefs,"paths-kebab-case":j.PathsKebabCase,"boolean-parameter-prefixes":R.BooleanParameterPrefixes,"path-http-verbs-order":T.PathHttpVerbsOrder,"no-invalid-media-type-examples":N.ValidContentExamples,"no-identical-paths":D.NoIdenticalPaths,"no-ambiguous-paths":z.NoAmbiguousPaths,"no-undefined-server-variable":L.NoUndefinedServerVariable,"no-servers-empty-enum":U.NoEmptyEnumServers,"no-http-verbs-in-paths":V.NoHttpVerbsInPaths,"path-excludes-patterns":H.PathExcludesPatterns,"request-mime-type":B.RequestMimeType,"response-mime-type":q.ResponseMimeType,"path-segment-plural":W.PathSegmentPlural,"no-invalid-schema-examples":Y.NoInvalidSchemaExamples,"no-invalid-parameter-examples":K.NoInvalidParameterExamples,"response-contains-header":G.ResponseContainsHeader,"response-contains-property":Q.ResponseContainsProperty,"scalar-property-missing-example":X.ScalarPropertyMissingExample},t.preprocessors={}},7020:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoEmptyServers=void 0,t.NoEmptyServers=()=>({DefinitionRoot(e,{report:t,location:n}){e.hasOwnProperty("servers")?Array.isArray(e.servers)&&0!==e.servers.length||t({message:"Servers must be a non-empty array.",location:n.child(["servers"]).key()}):t({message:"Servers must be present.",location:n.child(["openapi"]).key()})}})},1265:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoExampleValueAndExternalValue=void 0,t.NoExampleValueAndExternalValue=()=>({Example(e,{report:t,location:n}){e.value&&e.externalValue&&t({message:"Example object can have either `value` or `externalValue` fields.",location:n.child(["value"]).key()})}})},9336:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ValidContentExamples=void 0;const r=n(7468),o=n(780);t.ValidContentExamples=e=>{var t;const n=null===(t=e.disallowAdditionalProperties)||void 0===t||t;return{MediaType:{leave(e,t){const{location:i,resolve:a}=t;if(e.schema)if(e.example)s(e.example,i.child("example"));else if(e.examples)for(const t of Object.keys(e.examples))s(e.examples[t],i.child(["examples",t,"value"]),!0);function s(i,s,l){if(r.isRef(i)){const e=a(i);if(!e.location)return;s=l?e.location.child("value"):e.location,i=e.node}o.validateExample(l?i.value:i,e.schema,s,t,n)}}}}}},5486:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoServerExample=void 0,t.NoServerExample=()=>({Server(e,{report:t,location:n}){-1!==["example.com","localhost"].indexOf(e.url)&&t({message:"Server `url` should not point at example.com.",location:n.child(["url"])})}})},2947:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoServerTrailingSlash=void 0,t.NoServerTrailingSlash=()=>({Server(e,{report:t,location:n}){e.url&&e.url.endsWith("/")&&"/"!==e.url&&t({message:"Server `url` should not have a trailing slash.",location:n.child(["url"])})}})},472:function(e,t){"use strict";var n;function r(e){var t;if(e.variables&&0===Object.keys(e.variables).length)return;const r=[];for(var o in e.variables){const i=e.variables[o];if(!i.enum)continue;if(Array.isArray(i.enum)&&0===(null===(t=i.enum)||void 0===t?void 0:t.length)&&r.push(n.empty),!i.default)continue;const a=e.variables[o].default;i.enum&&!i.enum.includes(a)&&r.push(n.invalidDefaultValue)}return r.length?r:void 0}Object.defineProperty(t,"__esModule",{value:!0}),t.NoEmptyEnumServers=void 0,function(e){e.empty="empty",e.invalidDefaultValue="invalidDefaultValue"}(n||(n={})),t.NoEmptyEnumServers=()=>({DefinitionRoot(e,{report:t,location:o}){if(!e.servers||0===e.servers.length)return;const i=[];if(Array.isArray(e.servers))for(const t of e.servers){const e=r(t);e&&i.push(...e)}else{const t=r(e.servers);if(!t)return;i.push(...t)}for(const e of i)e===n.empty&&t({message:"Server variable with `enum` must be a non-empty array.",location:o.child(["servers"]).key()}),e===n.invalidDefaultValue&&t({message:"Server variable define `enum` and `default`. `enum` must include default value",location:o.child(["servers"]).key()})}})},6208:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoUndefinedServerVariable=void 0,t.NoUndefinedServerVariable=()=>({Server(e,{report:t,location:n}){var r;if(!e.url)return;const o=(null===(r=e.url.match(/{[^}]+}/g))||void 0===r?void 0:r.map((e=>e.slice(1,e.length-1))))||[],i=(null==e?void 0:e.variables)&&Object.keys(e.variables)||[];for(const e of o)i.includes(e)||t({message:`The \`${e}\` variable is not defined in the \`variables\` objects.`,location:n.child(["url"])});for(const e of i)o.includes(e)||t({message:`The \`${e}\` variable is not used in the server's \`url\` field.`,location:n.child(["variables",e]).key(),from:n.child("url")})}})},897:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoUnusedComponents=void 0,t.NoUnusedComponents=()=>{let e=new Map;function t(t,n){var r;e.set(t.absolutePointer,{used:(null===(r=e.get(t.absolutePointer))||void 0===r?void 0:r.used)||!1,location:t,name:n})}return{ref(t,{type:n,resolve:r,key:o,location:i}){if(["Schema","Header","Parameter","Response","Example","RequestBody"].includes(n.name)){const n=r(t);if(!n.location)return;e.set(n.location.absolutePointer,{used:!0,name:o.toString(),location:i})}},DefinitionRoot:{leave(t,{report:n}){e.forEach((e=>{e.used||n({message:`Component: "${e.name}" is never used.`,location:e.location.key()})}))}},NamedSchemas:{Schema(e,{location:n,key:r}){e.allOf||t(n,r.toString())}},NamedParameters:{Parameter(e,{location:n,key:r}){t(n,r.toString())}},NamedResponses:{Response(e,{location:n,key:r}){t(n,r.toString())}},NamedExamples:{Example(e,{location:n,key:r}){t(n,r.toString())}},NamedRequestBodies:{RequestBody(e,{location:n,key:r}){t(n,r.toString())}},NamedHeaders:{Header(e,{location:n,key:r}){t(n,r.toString())}}}}},6350:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RemoveUnusedComponents=void 0;const r=n(771);t.RemoveUnusedComponents=()=>{let e=new Map;function t(t,n,r){var o;e.set(t.absolutePointer,{used:(null===(o=e.get(t.absolutePointer))||void 0===o?void 0:o.used)||!1,componentType:n,name:r})}return{ref:{leave(t,{type:n,resolve:r,key:o}){if(["Schema","Header","Parameter","Response","Example","RequestBody"].includes(n.name)){const n=r(t);if(!n.location)return;e.set(n.location.absolutePointer,{used:!0,name:o.toString()})}}},DefinitionRoot:{leave(t,n){const o=n.getVisitorData();o.removedCount=0,e.forEach((e=>{const{used:n,componentType:i,name:a}=e;if(!n&&i){let e=t.components[i];delete e[a],o.removedCount++,r.isEmptyObject(e)&&delete t.components[i]}})),r.isEmptyObject(t.components)&&delete t.components}},NamedSchemas:{Schema(e,{location:n,key:r}){e.allOf||t(n,"schemas",r.toString())}},NamedParameters:{Parameter(e,{location:n,key:r}){t(n,"parameters",r.toString())}},NamedResponses:{Response(e,{location:n,key:r}){t(n,"responses",r.toString())}},NamedExamples:{Example(e,{location:n,key:r}){t(n,"examples",r.toString())}},NamedRequestBodies:{RequestBody(e,{location:n,key:r}){t(n,"requestBodies",r.toString())}},NamedHeaders:{Header(e,{location:n,key:r}){t(n,"headers",r.toString())}}}}},3736:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RequestMimeType=void 0;const r=n(771);t.RequestMimeType=({allowedValues:e})=>({PathMap:{RequestBody:{leave(t,n){r.validateMimeTypeOAS3({type:"consumes",value:t},n,e)}},Callback:{RequestBody(){},Response:{leave(t,n){r.validateMimeTypeOAS3({type:"consumes",value:t},n,e)}}}},WebhooksMap:{Response:{leave(t,n){r.validateMimeTypeOAS3({type:"consumes",value:t},n,e)}}}})},7557:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ResponseContainsProperty=void 0;const r=n(771);t.ResponseContainsProperty=e=>{const t=e.names||{};let n;return{Operation:{Response:{skip:(e,t)=>"204"==`${t}`,enter:(e,t)=>{n=t.key},MediaType:{Schema(e,{report:o,location:i}){var a;if("object"!==e.type)return;const s=t[n]||t[r.getMatchingStatusCodeRange(n)]||t[r.getMatchingStatusCodeRange(n).toLowerCase()]||[];for(const t of s)(null===(a=e.properties)||void 0===a?void 0:a[t])||o({message:`Response object must contain a top-level "${t}" property.`,location:i.child("properties").key()})}}}}}}},503:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ResponseMimeType=void 0;const r=n(771);t.ResponseMimeType=({allowedValues:e})=>({PathMap:{Response:{leave(t,n){r.validateMimeTypeOAS3({type:"produces",value:t},n,e)}},Callback:{Response(){},RequestBody:{leave(t,n){r.validateMimeTypeOAS3({type:"produces",value:t},n,e)}}}},WebhooksMap:{RequestBody:{leave(t,n){r.validateMimeTypeOAS3({type:"produces",value:t},n,e)}}}})},780:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateExample=t.getSuggest=t.validateDefinedAndNonEmpty=t.fieldNonEmpty=t.missingRequiredField=t.matchesJsonSchemaType=t.oasTypeOf=void 0;const r=n(9991),o=n(7468),i=n(7275);function a(e,t){return`${e} object should contain \`${t}\` field.`}function s(e,t){return`${e} object \`${t}\` must be non-empty string.`}t.oasTypeOf=function(e){return Array.isArray(e)?"array":null===e?"null":typeof e},t.matchesJsonSchemaType=function(e,t,n){if(n&&null===e)return null===e;switch(t){case"array":return Array.isArray(e);case"object":return"object"==typeof e&&null!==e&&!Array.isArray(e);case"null":return null===e;case"integer":return Number.isInteger(e);default:return typeof e===t}},t.missingRequiredField=a,t.fieldNonEmpty=s,t.validateDefinedAndNonEmpty=function(e,t,n){"object"==typeof t&&(void 0===t[e]?n.report({message:a(n.type.name,e),location:n.location.child([e]).key()}):t[e]||n.report({message:s(n.type.name,e),location:n.location.child([e]).key()}))},t.getSuggest=function(e,t){if("string"!=typeof e||!t.length)return[];const n=[];for(let o=0;oe.distance-t.distance)),n.map((e=>e.variant))},t.validateExample=function(e,t,n,{resolve:r,location:a,report:s},l){try{const{valid:c,errors:u}=i.validateJsonSchema(e,t,a.child("schema"),n.pointer,r,l);if(!c)for(let e of u)s({message:`Example value must conform to the schema: ${e.message}.`,location:Object.assign(Object.assign({},new o.Location(n.source,e.instancePath)),{reportOnKey:"additionalProperties"===e.keyword}),from:a,suggest:e.suggest})}catch(e){s({message:`Example validation errored: ${e.message}.`,location:a.child("schema"),from:a})}}},5220:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isNamedType=t.normalizeTypes=t.mapOf=t.listOf=void 0,t.listOf=function(e){return{name:`${e}List`,properties:{},items:e}},t.mapOf=function(e){return{name:`${e}Map`,properties:{},additionalProperties:()=>e}},t.normalizeTypes=function(e,t={}){const n={};for(const t of Object.keys(e))n[t]=Object.assign(Object.assign({},e[t]),{name:t});for(const e of Object.values(n))r(e);return n;function r(e){if(e.additionalProperties&&(e.additionalProperties=o(e.additionalProperties)),e.items&&(e.items=o(e.items)),e.properties){const n={};for(const[r,i]of Object.entries(e.properties))n[r]=o(i),t.doNotResolveExamples&&i&&i.isExample&&(n[r]=Object.assign(Object.assign({},i),{resolvable:!1}));e.properties=n}}function o(e){if("string"==typeof e){if(!n[e])throw new Error(`Unknown type name found: ${e}`);return n[e]}return"function"==typeof e?(t,n)=>o(e(t,n)):e&&e.name?(r(e=Object.assign({},e)),e):e&&e.directResolveAs?Object.assign(Object.assign({},e),{directResolveAs:o(e.directResolveAs)}):e}},t.isNamedType=function(e){return"string"==typeof(null==e?void 0:e.name)}},388:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Oas2Types=void 0;const r=n(5220),o=/^[0-9][0-9Xx]{2}$/,i={properties:{swagger:{type:"string"},info:"Info",host:{type:"string"},basePath:{type:"string"},schemes:{type:"array",items:{type:"string"}},consumes:{type:"array",items:{type:"string"}},produces:{type:"array",items:{type:"string"}},paths:"PathMap",definitions:"NamedSchemas",parameters:"NamedParameters",responses:"NamedResponses",securityDefinitions:"NamedSecuritySchemes",security:r.listOf("SecurityRequirement"),tags:r.listOf("Tag"),externalDocs:"ExternalDocs"},required:["swagger","paths","info"]},a={properties:{$ref:{type:"string"},parameters:r.listOf("Parameter"),get:"Operation",put:"Operation",post:"Operation",delete:"Operation",options:"Operation",head:"Operation",patch:"Operation"}},s={properties:{tags:{type:"array",items:{type:"string"}},summary:{type:"string"},description:{type:"string"},externalDocs:"ExternalDocs",operationId:{type:"string"},consumes:{type:"array",items:{type:"string"}},produces:{type:"array",items:{type:"string"}},parameters:r.listOf("Parameter"),responses:"ResponsesMap",schemes:{type:"array",items:{type:"string"}},deprecated:{type:"boolean"},security:r.listOf("SecurityRequirement"),"x-codeSamples":r.listOf("XCodeSample"),"x-code-samples":r.listOf("XCodeSample"),"x-hideTryItPanel":{type:"boolean"}},required:["responses"]},l={properties:{default:"Response"},additionalProperties:(e,t)=>o.test(t)?"Response":void 0},c={properties:{description:{type:"string"},schema:"Schema",headers:r.mapOf("Header"),examples:"Examples"},required:["description"]},u={properties:{format:{type:"string"},title:{type:"string"},description:{type:"string"},default:null,multipleOf:{type:"number"},maximum:{type:"number"},minimum:{type:"number"},exclusiveMaximum:{type:"boolean"},exclusiveMinimum:{type:"boolean"},maxLength:{type:"number"},minLength:{type:"number"},pattern:{type:"string"},maxItems:{type:"number"},minItems:{type:"number"},uniqueItems:{type:"boolean"},maxProperties:{type:"number"},minProperties:{type:"number"},required:{type:"array",items:{type:"string"}},enum:{type:"array"},type:{type:"string",enum:["object","array","string","number","integer","boolean","null"]},items:e=>Array.isArray(e)?r.listOf("Schema"):"Schema",allOf:r.listOf("Schema"),properties:"SchemaProperties",additionalProperties:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",discriminator:{type:"string"},readOnly:{type:"boolean"},xml:"Xml",externalDocs:"ExternalDocs",example:{isExample:!0},"x-tags":{type:"array",items:{type:"string"}}}},p={properties:{type:{enum:["basic","apiKey","oauth2"]},description:{type:"string"},name:{type:"string"},in:{type:"string",enum:["query","header"]},flow:{enum:["implicit","password","application","accessCode"]},authorizationUrl:{type:"string"},tokenUrl:{type:"string"},scopes:{type:"object",additionalProperties:{type:"string"}}},required(e){switch(null==e?void 0:e.type){case"apiKey":return["type","name","in"];case"oauth2":switch(null==e?void 0:e.flow){case"implicit":return["type","flow","authorizationUrl","scopes"];case"accessCode":return["type","flow","authorizationUrl","tokenUrl","scopes"];case"application":case"password":return["type","flow","tokenUrl","scopes"];default:return["type","flow","scopes"]}default:return["type"]}},allowed(e){switch(null==e?void 0:e.type){case"basic":return["type","description"];case"apiKey":return["type","name","in","description"];case"oauth2":switch(null==e?void 0:e.flow){case"implicit":return["type","flow","authorizationUrl","description","scopes"];case"accessCode":return["type","flow","authorizationUrl","tokenUrl","description","scopes"];case"application":case"password":return["type","flow","tokenUrl","description","scopes"];default:return["type","flow","tokenUrl","authorizationUrl","description","scopes"]}default:return["type","description"]}},extensionsPrefix:"x-"};t.Oas2Types={DefinitionRoot:i,Tag:{properties:{name:{type:"string"},description:{type:"string"},externalDocs:"ExternalDocs"},required:["name"]},ExternalDocs:{properties:{description:{type:"string"},url:{type:"string"}},required:["url"]},SecurityRequirement:{properties:{},additionalProperties:{type:"array",items:{type:"string"}}},Info:{properties:{title:{type:"string"},description:{type:"string"},termsOfService:{type:"string"},contact:"Contact",license:"License",version:{type:"string"}},required:["title","version"]},Contact:{properties:{name:{type:"string"},url:{type:"string"},email:{type:"string"}}},License:{properties:{name:{type:"string"},url:{type:"string"}},required:["name"]},PathMap:{properties:{},additionalProperties:(e,t)=>t.startsWith("/")?"PathItem":void 0},PathItem:a,Parameter:{properties:{name:{type:"string"},in:{type:"string",enum:["query","header","path","formData","body"]},description:{type:"string"},required:{type:"boolean"},schema:"Schema",type:{type:"string",enum:["string","number","integer","boolean","array","file"]},format:{type:"string"},allowEmptyValue:{type:"boolean"},items:"ParameterItems",collectionFormat:{type:"string",enum:["csv","ssv","tsv","pipes","multi"]},default:null,maximum:{type:"integer"},exclusiveMaximum:{type:"boolean"},minimum:{type:"integer"},exclusiveMinimum:{type:"boolean"},maxLength:{type:"integer"},minLength:{type:"integer"},pattern:{type:"string"},maxItems:{type:"integer"},minItems:{type:"integer"},uniqueItems:{type:"boolean"},enum:{type:"array"},multipleOf:{type:"number"}},required:e=>e&&e.in?"body"===e.in?["name","in","schema"]:"array"===e.type?["name","in","type","items"]:["name","in","type"]:["name","in"]},ParameterItems:{properties:{type:{type:"string",enum:["string","number","integer","boolean","array"]},format:{type:"string"},items:"ParameterItems",collectionFormat:{type:"string",enum:["csv","ssv","tsv","pipes","multi"]},default:null,maximum:{type:"integer"},exclusiveMaximum:{type:"boolean"},minimum:{type:"integer"},exclusiveMinimum:{type:"boolean"},maxLength:{type:"integer"},minLength:{type:"integer"},pattern:{type:"string"},maxItems:{type:"integer"},minItems:{type:"integer"},uniqueItems:{type:"boolean"},enum:{type:"array"},multipleOf:{type:"number"}},required:e=>e&&"array"===e.type?["type","items"]:["type"]},Operation:s,Examples:{properties:{},additionalProperties:{isExample:!0}},Header:{properties:{description:{type:"string"},type:{type:"string",enum:["string","number","integer","boolean","array"]},format:{type:"string"},items:"ParameterItems",collectionFormat:{type:"string",enum:["csv","ssv","tsv","pipes","multi"]},default:null,maximum:{type:"integer"},exclusiveMaximum:{type:"boolean"},minimum:{type:"integer"},exclusiveMinimum:{type:"boolean"},maxLength:{type:"integer"},minLength:{type:"integer"},pattern:{type:"string"},maxItems:{type:"integer"},minItems:{type:"integer"},uniqueItems:{type:"boolean"},enum:{type:"array"},multipleOf:{type:"number"}},required:e=>e&&"array"===e.type?["type","items"]:["type"]},ResponsesMap:l,Response:c,Schema:u,Xml:{properties:{name:{type:"string"},namespace:{type:"string"},prefix:{type:"string"},attribute:{type:"boolean"},wrapped:{type:"boolean"}}},SchemaProperties:{properties:{},additionalProperties:"Schema"},NamedSchemas:r.mapOf("Schema"),NamedResponses:r.mapOf("Response"),NamedParameters:r.mapOf("Parameter"),NamedSecuritySchemes:r.mapOf("SecurityScheme"),SecurityScheme:p,XCodeSample:{properties:{lang:{type:"string"},label:{type:"string"},source:{type:"string"}}}}},5241:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Oas3Types=void 0;const r=n(5220),o=n(7468),i=/^[0-9][0-9Xx]{2}$/,a={properties:{openapi:null,info:"Info",servers:r.listOf("Server"),security:r.listOf("SecurityRequirement"),tags:r.listOf("Tag"),externalDocs:"ExternalDocs",paths:"PathMap",components:"Components","x-webhooks":"WebhooksMap"},required:["openapi","paths","info"]},s={properties:{url:{type:"string"},description:{type:"string"},variables:r.mapOf("ServerVariable")},required:["url"]},l={properties:{$ref:{type:"string"},servers:r.listOf("Server"),parameters:r.listOf("Parameter"),summary:{type:"string"},description:{type:"string"},get:"Operation",put:"Operation",post:"Operation",delete:"Operation",options:"Operation",head:"Operation",patch:"Operation",trace:"Operation"}},c={properties:{name:{type:"string"},in:{enum:["query","header","path","cookie"]},description:{type:"string"},required:{type:"boolean"},deprecated:{type:"boolean"},allowEmptyValue:{type:"boolean"},style:{enum:["form","simple","label","matrix","spaceDelimited","pipeDelimited","deepObject"]},explode:{type:"boolean"},allowReserved:{type:"boolean"},schema:"Schema",example:{isExample:!0},examples:r.mapOf("Example"),content:"MediaTypeMap"},required:["name","in"],requiredOneOf:["schema","content"]},u={properties:{tags:{type:"array",items:{type:"string"}},summary:{type:"string"},description:{type:"string"},externalDocs:"ExternalDocs",operationId:{type:"string"},parameters:r.listOf("Parameter"),security:r.listOf("SecurityRequirement"),servers:r.listOf("Server"),requestBody:"RequestBody",responses:"ResponsesMap",deprecated:{type:"boolean"},callbacks:r.mapOf("Callback"),"x-codeSamples":r.listOf("XCodeSample"),"x-code-samples":r.listOf("XCodeSample"),"x-hideTryItPanel":{type:"boolean"}},required:["responses"]},p={properties:{schema:"Schema",example:{isExample:!0},examples:r.mapOf("Example"),encoding:r.mapOf("Encoding")}},d={properties:{contentType:{type:"string"},headers:r.mapOf("Header"),style:{enum:["form","simple","label","matrix","spaceDelimited","pipeDelimited","deepObject"]},explode:{type:"boolean"},allowReserved:{type:"boolean"}}},f={properties:{description:{type:"string"},required:{type:"boolean"},deprecated:{type:"boolean"},allowEmptyValue:{type:"boolean"},style:{enum:["form","simple","label","matrix","spaceDelimited","pipeDelimited","deepObject"]},explode:{type:"boolean"},allowReserved:{type:"boolean"},schema:"Schema",example:{isExample:!0},examples:r.mapOf("Example"),content:"MediaTypeMap"}},h={properties:{default:"Response"},additionalProperties:(e,t)=>i.test(t)?"Response":void 0},m={properties:{description:{type:"string"},headers:r.mapOf("Header"),content:"MediaTypeMap",links:r.mapOf("Link")},required:["description"]},g={properties:{externalDocs:"ExternalDocs",discriminator:"Discriminator",title:{type:"string"},multipleOf:{type:"number",minimum:0},maximum:{type:"number"},minimum:{type:"number"},exclusiveMaximum:{type:"boolean"},exclusiveMinimum:{type:"boolean"},maxLength:{type:"integer",minimum:0},minLength:{type:"integer",minimum:0},pattern:{type:"string"},maxItems:{type:"integer",minimum:0},minItems:{type:"integer",minimum:0},uniqueItems:{type:"boolean"},maxProperties:{type:"integer",minimum:0},minProperties:{type:"integer",minimum:0},required:{type:"array",items:{type:"string"}},enum:{type:"array"},type:{enum:["object","array","string","number","integer","boolean","null"]},allOf:r.listOf("Schema"),anyOf:r.listOf("Schema"),oneOf:r.listOf("Schema"),not:"Schema",properties:"SchemaProperties",items:e=>Array.isArray(e)?r.listOf("Schema"):"Schema",additionalItems:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",additionalProperties:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",description:{type:"string"},format:{type:"string"},default:null,nullable:{type:"boolean"},readOnly:{type:"boolean"},writeOnly:{type:"boolean"},xml:"Xml",example:{isExample:!0},deprecated:{type:"boolean"},"x-tags":{type:"array",items:{type:"string"}}}},y={properties:{},additionalProperties:e=>o.isMappingRef(e)?{type:"string",directResolveAs:"Schema"}:{type:"string"}},v={properties:{type:{enum:["apiKey","http","oauth2","openIdConnect"]},description:{type:"string"},name:{type:"string"},in:{type:"string",enum:["query","header","cookie"]},scheme:{type:"string"},bearerFormat:{type:"string"},flows:"SecuritySchemeFlows",openIdConnectUrl:{type:"string"}},required(e){switch(null==e?void 0:e.type){case"apiKey":return["type","name","in"];case"http":return["type","scheme"];case"oauth2":return["type","flows"];case"openIdConnect":return["type","openIdConnectUrl"];default:return["type"]}},allowed(e){switch(null==e?void 0:e.type){case"apiKey":return["type","name","in","description"];case"http":return["type","scheme","bearerFormat","description"];case"oauth2":return["type","flows","description"];case"openIdConnect":return["type","openIdConnectUrl","description"];default:return["type","description"]}},extensionsPrefix:"x-"};t.Oas3Types={DefinitionRoot:a,Tag:{properties:{name:{type:"string"},description:{type:"string"},externalDocs:"ExternalDocs"},required:["name"]},ExternalDocs:{properties:{description:{type:"string"},url:{type:"string"}},required:["url"]},Server:s,ServerVariable:{properties:{enum:{type:"array",items:{type:"string"}},default:{type:"string"},description:null},required:["default"]},SecurityRequirement:{properties:{},additionalProperties:{type:"array",items:{type:"string"}}},Info:{properties:{title:{type:"string"},version:{type:"string"},description:{type:"string"},termsOfService:{type:"string"},contact:"Contact",license:"License"},required:["title","version"]},Contact:{properties:{name:{type:"string"},url:{type:"string"},email:{type:"string"}}},License:{properties:{name:{type:"string"},url:{type:"string"}},required:["name"]},PathMap:{properties:{},additionalProperties:(e,t)=>t.startsWith("/")?"PathItem":void 0},PathItem:l,Parameter:c,Operation:u,Callback:r.mapOf("PathItem"),RequestBody:{properties:{description:{type:"string"},required:{type:"boolean"},content:"MediaTypeMap"},required:["content"]},MediaTypeMap:{properties:{},additionalProperties:"MediaType"},MediaType:p,Example:{properties:{value:{isExample:!0},summary:{type:"string"},description:{type:"string"},externalValue:{type:"string"}}},Encoding:d,Header:f,ResponsesMap:h,Response:m,Link:{properties:{operationRef:{type:"string"},operationId:{type:"string"},parameters:null,requestBody:null,description:{type:"string"},server:"Server"}},Schema:g,Xml:{properties:{name:{type:"string"},namespace:{type:"string"},prefix:{type:"string"},attribute:{type:"boolean"},wrapped:{type:"boolean"}}},SchemaProperties:{properties:{},additionalProperties:"Schema"},DiscriminatorMapping:y,Discriminator:{properties:{propertyName:{type:"string"},mapping:"DiscriminatorMapping"},required:["propertyName"]},Components:{properties:{parameters:"NamedParameters",schemas:"NamedSchemas",responses:"NamedResponses",examples:"NamedExamples",requestBodies:"NamedRequestBodies",headers:"NamedHeaders",securitySchemes:"NamedSecuritySchemes",links:"NamedLinks",callbacks:"NamedCallbacks"}},NamedSchemas:r.mapOf("Schema"),NamedResponses:r.mapOf("Response"),NamedParameters:r.mapOf("Parameter"),NamedExamples:r.mapOf("Example"),NamedRequestBodies:r.mapOf("RequestBody"),NamedHeaders:r.mapOf("Header"),NamedSecuritySchemes:r.mapOf("SecurityScheme"),NamedLinks:r.mapOf("Link"),NamedCallbacks:r.mapOf("Callback"),ImplicitFlow:{properties:{refreshUrl:{type:"string"},scopes:{type:"object",additionalProperties:{type:"string"}},authorizationUrl:{type:"string"}},required:["authorizationUrl","scopes"]},PasswordFlow:{properties:{refreshUrl:{type:"string"},scopes:{type:"object",additionalProperties:{type:"string"}},tokenUrl:{type:"string"}},required:["tokenUrl","scopes"]},ClientCredentials:{properties:{refreshUrl:{type:"string"},scopes:{type:"object",additionalProperties:{type:"string"}},tokenUrl:{type:"string"}},required:["tokenUrl","scopes"]},AuthorizationCode:{properties:{refreshUrl:{type:"string"},authorizationUrl:{type:"string"},scopes:{type:"object",additionalProperties:{type:"string"}},tokenUrl:{type:"string"}},required:["authorizationUrl","tokenUrl","scopes"]},SecuritySchemeFlows:{properties:{implicit:"ImplicitFlow",password:"PasswordFlow",clientCredentials:"ClientCredentials",authorizationCode:"AuthorizationCode"}},SecurityScheme:v,XCodeSample:{properties:{lang:{type:"string"},label:{type:"string"},source:{type:"string"}}},WebhooksMap:{properties:{},additionalProperties:()=>"PathItem"}}},2608:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Oas3_1Types=void 0;const r=n(5220),o=n(5241),i={properties:{openapi:null,info:"Info",servers:r.listOf("Server"),security:r.listOf("SecurityRequirement"),tags:r.listOf("Tag"),externalDocs:"ExternalDocs",paths:"PathMap",webhooks:"WebhooksMap",components:"Components",jsonSchemaDialect:{type:"string"}},required:["openapi","info"],requiredOneOf:["paths","components","webhooks"]},a={properties:{tags:{type:"array",items:{type:"string"}},summary:{type:"string"},description:{type:"string"},externalDocs:"ExternalDocs",operationId:{type:"string"},parameters:r.listOf("Parameter"),security:r.listOf("SecurityRequirement"),servers:r.listOf("Server"),requestBody:"RequestBody",responses:"ResponsesMap",deprecated:{type:"boolean"},callbacks:r.mapOf("Callback"),"x-codeSamples":r.listOf("XCodeSample"),"x-code-samples":r.listOf("XCodeSample"),"x-hideTryItPanel":{type:"boolean"}}},s={properties:{$id:{type:"string"},id:{type:"string"},$schema:{type:"string"},definitions:"NamedSchemas",$defs:"NamedSchemas",$vocabulary:{type:"string"},externalDocs:"ExternalDocs",discriminator:"Discriminator",myArbitraryKeyword:{type:"boolean"},title:{type:"string"},multipleOf:{type:"number",minimum:0},maximum:{type:"number"},minimum:{type:"number"},exclusiveMaximum:{type:"number"},exclusiveMinimum:{type:"number"},maxLength:{type:"integer",minimum:0},minLength:{type:"integer",minimum:0},pattern:{type:"string"},maxItems:{type:"integer",minimum:0},minItems:{type:"integer",minimum:0},uniqueItems:{type:"boolean"},maxProperties:{type:"integer",minimum:0},minProperties:{type:"integer",minimum:0},required:{type:"array",items:{type:"string"}},enum:{type:"array"},type:e=>Array.isArray(e)?{type:"array",items:{enum:["object","array","string","number","integer","boolean","null"]}}:{enum:["object","array","string","number","integer","boolean","null"]},allOf:r.listOf("Schema"),anyOf:r.listOf("Schema"),oneOf:r.listOf("Schema"),not:"Schema",if:"Schema",then:"Schema",else:"Schema",dependentSchemas:r.listOf("Schema"),prefixItems:r.listOf("Schema"),contains:"Schema",minContains:{type:"integer",minimum:0},maxContains:{type:"integer",minimum:0},patternProperties:{type:"object"},propertyNames:"Schema",unevaluatedItems:"Schema",unevaluatedProperties:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",summary:{type:"string"},properties:"SchemaProperties",items:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",additionalProperties:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",description:{type:"string"},format:{type:"string"},contentEncoding:{type:"string"},contentMediaType:{type:"string"},default:null,readOnly:{type:"boolean"},writeOnly:{type:"boolean"},xml:"Xml",examples:{type:"array"},example:{isExample:!0},deprecated:{type:"boolean"},const:null,$comment:{type:"string"},"x-tags":{type:"array",items:{type:"string"}}}},l={properties:{type:{enum:["apiKey","http","oauth2","openIdConnect","mutualTLS"]},description:{type:"string"},name:{type:"string"},in:{type:"string",enum:["query","header","cookie"]},scheme:{type:"string"},bearerFormat:{type:"string"},flows:"SecuritySchemeFlows",openIdConnectUrl:{type:"string"}},required(e){switch(null==e?void 0:e.type){case"apiKey":return["type","name","in"];case"http":return["type","scheme"];case"oauth2":return["type","flows"];case"openIdConnect":return["type","openIdConnectUrl"];default:return["type"]}},allowed(e){switch(null==e?void 0:e.type){case"apiKey":return["type","name","in","description"];case"http":return["type","scheme","bearerFormat","description"];case"oauth2":switch(null==e?void 0:e.flows){case"implicit":return["type","flows","authorizationUrl","refreshUrl","description","scopes"];case"password":case"clientCredentials":return["type","flows","tokenUrl","refreshUrl","description","scopes"];default:return["type","flows","authorizationUrl","refreshUrl","tokenUrl","description","scopes"]}case"openIdConnect":return["type","openIdConnectUrl","description"];default:return["type","description"]}},extensionsPrefix:"x-"};t.Oas3_1Types=Object.assign(Object.assign({},o.Oas3Types),{Info:{properties:{title:{type:"string"},version:{type:"string"},description:{type:"string"},termsOfService:{type:"string"},summary:{type:"string"},contact:"Contact",license:"License"},required:["title","version"]},DefinitionRoot:i,Schema:s,License:{properties:{name:{type:"string"},url:{type:"string"},identifier:{type:"string"}},required:["name"]},Components:{properties:{parameters:"NamedParameters",schemas:"NamedSchemas",responses:"NamedResponses",examples:"NamedExamples",requestBodies:"NamedRequestBodies",headers:"NamedHeaders",securitySchemes:"NamedSecuritySchemes",links:"NamedLinks",callbacks:"NamedCallbacks",pathItems:"NamedPathItems"}},NamedPathItems:r.mapOf("PathItem"),SecurityScheme:l,Operation:a})},771:function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,i){function a(e){try{l(r.next(e))}catch(e){i(e)}}function s(e){try{l(r.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}l((r=r.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.isCustomRuleId=t.getMatchingStatusCodeRange=t.assignExisting=t.isNotString=t.isString=t.isNotEmptyObject=t.slash=t.isPathParameter=t.readFileAsStringSync=t.isSingular=t.validateMimeTypeOAS3=t.validateMimeType=t.splitCamelCaseIntoWords=t.omitObjectProps=t.pickObjectProps=t.readFileFromUrl=t.isEmptyArray=t.isEmptyObject=t.isPlainObject=t.notUndefined=t.loadYaml=t.popStack=t.pushStack=t.stringifyYaml=t.parseYaml=void 0;const o=n(3197),i=n(4099),a=n(8150),s=n(3450),l=n(5273),c=n(8698);var u=n(5273);function p(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}function d(e,t){return t.match(/^https?:\/\//)||(e=e.replace(/^https?:\/\//,"")),i(e,t)}function f(e){return"string"==typeof e}Object.defineProperty(t,"parseYaml",{enumerable:!0,get:function(){return u.parseYaml}}),Object.defineProperty(t,"stringifyYaml",{enumerable:!0,get:function(){return u.stringifyYaml}}),t.pushStack=function(e,t){return{prev:e,value:t}},t.popStack=function(e){var t;return null!==(t=null==e?void 0:e.prev)&&void 0!==t?t:null},t.loadYaml=function(e){return r(this,void 0,void 0,(function*(){const t=yield o.promises.readFile(e,"utf-8");return l.parseYaml(t)}))},t.notUndefined=function(e){return void 0!==e},t.isPlainObject=p,t.isEmptyObject=function(e){return p(e)&&0===Object.keys(e).length},t.isEmptyArray=function(e){return Array.isArray(e)&&0===e.length},t.readFileFromUrl=function(e,t){return r(this,void 0,void 0,(function*(){const n={};for(const r of t.headers)d(e,r.matches)&&(n[r.name]=void 0!==r.envVariable?c.env[r.envVariable]||"":r.value);const r=yield(t.customFetch||a.default)(e,{headers:n});if(!r.ok)throw new Error(`Failed to load ${e}: ${r.status} ${r.statusText}`);return{body:yield r.text(),mimeType:r.headers.get("content-type")}}))},t.pickObjectProps=function(e,t){return Object.fromEntries(t.filter((t=>t in e)).map((t=>[t,e[t]])))},t.omitObjectProps=function(e,t){return Object.fromEntries(Object.entries(e).filter((([e])=>!t.includes(e))))},t.splitCamelCaseIntoWords=function(e){const t=e.split(/(?:[-._])|([A-Z][a-z]+)/).filter(Boolean).map((e=>e.toLocaleLowerCase())),n=e.split(/([A-Z]{2,})/).filter((e=>e&&e===e.toUpperCase())).map((e=>e.toLocaleLowerCase()));return new Set([...t,...n])},t.validateMimeType=function({type:e,value:t},{report:n,location:r},o){if(!o)throw new Error(`Parameter "allowedValues" is not provided for "${"consumes"===e?"request":"response"}-mime-type" rule`);if(t[e])for(const i of t[e])o.includes(i)||n({message:`Mime type "${i}" is not allowed`,location:r.child(t[e].indexOf(i)).key()})},t.validateMimeTypeOAS3=function({type:e,value:t},{report:n,location:r},o){if(!o)throw new Error(`Parameter "allowedValues" is not provided for "${"consumes"===e?"request":"response"}-mime-type" rule`);if(t.content)for(const e of Object.keys(t.content))o.includes(e)||n({message:`Mime type "${e}" is not allowed`,location:r.child("content").child(e).key()})},t.isSingular=function(e){return s.isSingular(e)},t.readFileAsStringSync=function(e){return o.readFileSync(e,"utf-8")},t.isPathParameter=function(e){return e.startsWith("{")&&e.endsWith("}")},t.slash=function(e){return/^\\\\\?\\/.test(e)?e:e.replace(/\\/g,"/")},t.isNotEmptyObject=function(e){return!!e&&Object.keys(e).length>0},t.isString=f,t.isNotString=function(e){return!f(e)},t.assignExisting=function(e,t){for(let n of Object.keys(t))e.hasOwnProperty(n)&&(e[n]=t[n])},t.getMatchingStatusCodeRange=e=>`${e}`.replace(/^(\d)\d\d$/,((e,t)=>`${t}XX`)),t.isCustomRuleId=function(e){return e.includes("/")}},8065:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.normalizeVisitors=void 0,t.normalizeVisitors=function(e,t){const n={any:{enter:[],leave:[]}};for(const e of Object.keys(t))n[e]={enter:[],leave:[]};n.ref={enter:[],leave:[]};for(const{ruleId:t,severity:n,visitor:r}of e)o({ruleId:t,severity:n},r,null);for(const e of Object.keys(n))n[e].enter.sort(((e,t)=>t.depth-e.depth)),n[e].leave.sort(((e,t)=>e.depth-t.depth));return n;function r(e,t,o,i,a=[]){if(a.includes(t))return;a=[...a,t];const s=new Set;for(let n of Object.values(t.properties))n!==o?"object"==typeof n&&null!==n&&n.name&&s.add(n):l(e,a);t.additionalProperties&&"function"!=typeof t.additionalProperties&&(t.additionalProperties===o?l(e,a):void 0!==t.additionalProperties.name&&s.add(t.additionalProperties)),t.items&&(t.items===o?l(e,a):void 0!==t.items.name&&s.add(t.items));for(let t of Array.from(s.values()))r(e,t,o,i,a);function l(e,t){for(const r of t.slice(1))n[r.name]=n[r.name]||{enter:[],leave:[]},n[r.name].enter.push(Object.assign(Object.assign({},e),{visit:()=>{},depth:0,context:{isSkippedLevel:!0,seen:new Set,parent:i}}))}}function o(e,i,a,s=0){const l=Object.keys(t);if(0===s)l.push("any"),l.push("ref");else{if(i.any)throw new Error("any() is allowed only on top level");if(i.ref)throw new Error("ref() is allowed only on top level")}for(const c of l){const l=i[c],u=n[c];if(!l)continue;let p,d,f;const h="object"==typeof l;if("ref"===c&&h&&l.skip)throw new Error("ref() visitor does not support skip");"function"==typeof l?p=l:h&&(p=l.enter,d=l.leave,f=l.skip);const m={activatedOn:null,type:t[c],parent:a,isSkippedLevel:!1};if("object"==typeof l&&o(e,l,m,s+1),a&&r(e,a.type,t[c],a),p||h){if(p&&"function"!=typeof p)throw new Error("DEV: should be function");u.enter.push(Object.assign(Object.assign({},e),{visit:p||(()=>{}),skip:f,depth:s,context:m}))}if(d){if("function"!=typeof d)throw new Error("DEV: should be function");u.leave.push(Object.assign(Object.assign({},e),{visit:d,depth:s,context:m}))}}}}},9443:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.walkDocument=void 0;const r=n(7468),o=n(4182),i=n(771),a=n(5220);function s(e){var t,n;const r={};for(;e.parent;)(null===(t=e.parent.activatedOn)||void 0===t?void 0:t.value.location)&&(r[e.parent.type.name]=null===(n=e.parent.activatedOn)||void 0===n?void 0:n.value.location),e=e.parent;return r}t.walkDocument=function(e){const{document:t,rootType:n,normalizedVisitors:l,resolvedRefMap:c,ctx:u}=e,p={},d=new Set;!function e(t,n,f,h,m){var g,y,v,b,w,x,k,_,O,S,E;const P=(e,t=$.source.absoluteRef)=>{if(!r.isRef(e))return{location:f,node:e};const n=o.makeRefId(t,e.$ref),i=c.get(n);if(!i)return{location:void 0,node:void 0};const{resolved:a,node:s,document:l,nodePointer:u,error:p}=i;return{location:a?new r.Location(l.source,u):p instanceof o.YamlParseError?new r.Location(p.source,""):void 0,node:s,error:p}},A=f;let $=f;const{node:C,location:R,error:j}=P(t),T=new Set;if(r.isRef(t)){const e=l.ref.enter;for(const{visit:r,ruleId:o,severity:i,context:a}of e)if(!d.has(t)){T.add(a);r(t,{report:N.bind(void 0,o,i),resolve:P,rawNode:t,rawLocation:A,location:f,type:n,parent:h,key:m,parentLocations:{},oasVersion:u.oasVersion,getVisitorData:D.bind(void 0,o)},{node:C,location:R,error:j}),(null==R?void 0:R.source.absoluteRef)&&u.refTypes&&u.refTypes.set(null==R?void 0:R.source.absoluteRef,n)}}if(void 0!==C&&R&&"scalar"!==n.name){$=R;const o=null===(y=null===(g=p[n.name])||void 0===g?void 0:g.has)||void 0===y?void 0:y.call(g,C);let s=!1;const c=l.any.enter.concat((null===(v=l[n.name])||void 0===v?void 0:v.enter)||[]),u=[];for(const{context:e,visit:r,skip:a,ruleId:l,severity:p}of c)if(e.isSkippedLevel)!e.parent.activatedOn||e.parent.activatedOn.value.nextLevelTypeActivated||e.seen.has(t)||(e.seen.add(t),s=!0,u.push(e));else if(e.parent&&e.parent.activatedOn&&(null===(b=e.activatedOn)||void 0===b?void 0:b.value.withParentNode)!==e.parent.activatedOn.value.node&&(null===(w=e.parent.activatedOn.value.nextLevelTypeActivated)||void 0===w?void 0:w.value)!==n||!e.parent&&!o){u.push(e);const o={node:C,location:R,nextLevelTypeActivated:null,withParentNode:null===(k=null===(x=e.parent)||void 0===x?void 0:x.activatedOn)||void 0===k?void 0:k.value.node,skipped:null!==(S=(null===(O=null===(_=e.parent)||void 0===_?void 0:_.activatedOn)||void 0===O?void 0:O.value.skipped)||(null==a?void 0:a(C,m)))&&void 0!==S&&S};e.activatedOn=i.pushStack(e.activatedOn,o);let c=e.parent;for(;c;)c.activatedOn.value.nextLevelTypeActivated=i.pushStack(c.activatedOn.value.nextLevelTypeActivated,n),c=c.parent;if(!o.skipped){s=!0,T.add(e);const{ignoreNextVisitorsOnNode:n}=I(r,C,t,e,l,p);if(n)break}}if(s||!o)if(p[n.name]=p[n.name]||new Set,p[n.name].add(C),Array.isArray(C)){const t=n.items;if(void 0!==t)for(let n=0;n!o.includes(e)))),r.isRef(t)&&o.push(...Object.keys(t).filter((e=>"$ref"!==e&&!o.includes(e))));for(const i of o){let o=C[i],s=R;void 0===o&&(o=t[i],s=f);let l=n.properties[i];void 0===l&&(l=n.additionalProperties),"function"==typeof l&&(l=l(o,i)),!a.isNamedType(l)&&(null==l?void 0:l.directResolveAs)&&(l=l.directResolveAs,o={$ref:o}),l&&void 0===l.name&&!1!==l.resolvable&&(l={name:"scalar",properties:{}}),a.isNamedType(l)&&("scalar"!==l.name||r.isRef(o))&&e(o,l,s.child([i]),C,i)}}const d=l.any.leave,h=((null===(E=l[n.name])||void 0===E?void 0:E.leave)||[]).concat(d);for(const e of u.reverse())if(e.isSkippedLevel)e.seen.delete(C);else if(e.activatedOn=i.popStack(e.activatedOn),e.parent){let t=e.parent;for(;t;)t.activatedOn.value.nextLevelTypeActivated=i.popStack(t.activatedOn.value.nextLevelTypeActivated),t=t.parent}for(const{context:e,visit:n,ruleId:r,severity:o}of h)!e.isSkippedLevel&&T.has(e)&&I(n,C,t,e,r,o)}if($=f,r.isRef(t)){const e=l.ref.leave;for(const{visit:r,ruleId:o,severity:i,context:a}of e)if(T.has(a)){r(t,{report:N.bind(void 0,o,i),resolve:P,rawNode:t,rawLocation:A,location:f,type:n,parent:h,key:m,parentLocations:{},oasVersion:u.oasVersion,getVisitorData:D.bind(void 0,o)},{node:C,location:R,error:j})}}function I(e,t,r,o,i,a){const l=N.bind(void 0,i,a);let c=!1;return e(t,{report:l,resolve:P,rawNode:r,location:$,rawLocation:A,type:n,parent:h,key:m,parentLocations:s(o),oasVersion:u.oasVersion,ignoreNextVisitorsOnNode:()=>{c=!0},getVisitorData:D.bind(void 0,i)},function(e){var t;const n={};for(;e.parent;)n[e.parent.type.name]=null===(t=e.parent.activatedOn)||void 0===t?void 0:t.value.node,e=e.parent;return n}(o),o),{ignoreNextVisitorsOnNode:c}}function N(e,t,n){const r=n.location?Array.isArray(n.location)?n.location:[n.location]:[Object.assign(Object.assign({},$),{reportOnKey:!1})];u.problems.push(Object.assign(Object.assign({ruleId:n.ruleId||e,severity:n.forceSeverity||t},n),{suggest:n.suggest||[],location:r.map((e=>Object.assign(Object.assign(Object.assign({},$),{reportOnKey:!1}),e)))}))}function D(e){return u.visitorsData[e]=u.visitorsData[e]||{},u.visitorsData[e]}}(t.parsed,n,new r.Location(t.source,"#/"),void 0,"")}},5019:function(e,t,n){var r=n(5623);e.exports=function(e){return e?("{}"===e.substr(0,2)&&(e="\\{\\}"+e.substr(2)),g(function(e){return e.split("\\\\").join(o).split("\\{").join(i).split("\\}").join(a).split("\\,").join(s).split("\\.").join(l)}(e),!0).map(u)):[]};var o="\0SLASH"+Math.random()+"\0",i="\0OPEN"+Math.random()+"\0",a="\0CLOSE"+Math.random()+"\0",s="\0COMMA"+Math.random()+"\0",l="\0PERIOD"+Math.random()+"\0";function c(e){return parseInt(e,10)==e?parseInt(e,10):e.charCodeAt(0)}function u(e){return e.split(o).join("\\").split(i).join("{").split(a).join("}").split(s).join(",").split(l).join(".")}function p(e){if(!e)return[""];var t=[],n=r("{","}",e);if(!n)return e.split(",");var o=n.pre,i=n.body,a=n.post,s=o.split(",");s[s.length-1]+="{"+i+"}";var l=p(a);return a.length&&(s[s.length-1]+=l.shift(),s.push.apply(s,l)),t.push.apply(t,s),t}function d(e){return"{"+e+"}"}function f(e){return/^-?0\d/.test(e)}function h(e,t){return e<=t}function m(e,t){return e>=t}function g(e,t){var n=[],o=r("{","}",e);if(!o)return[e];var i=o.pre,s=o.post.length?g(o.post,!1):[""];if(/\$$/.test(o.pre))for(var l=0;l=0;if(!x&&!k)return o.post.match(/,.*\}/)?g(e=o.pre+"{"+o.body+a+o.post):[e];if(x)y=o.body.split(/\.\./);else if(1===(y=p(o.body)).length&&1===(y=g(y[0],!1).map(d)).length)return s.map((function(e){return o.pre+y[0]+e}));if(x){var _=c(y[0]),O=c(y[1]),S=Math.max(y[0].length,y[1].length),E=3==y.length?Math.abs(c(y[2])):1,P=h;O<_&&(E*=-1,P=m);var A=y.some(f);v=[];for(var $=_;P($,O);$+=E){var C;if(w)"\\"===(C=String.fromCharCode($))&&(C="");else if(C=String($),A){var R=S-C.length;if(R>0){var j=new Array(R+1).join("0");C=$<0?"-"+j+C.slice(1):j+C}}v.push(C)}}else{v=[];for(var T=0;T(g(t),!(!n.nocomment&&"#"===t.charAt(0))&&new v(t,n).match(e));e.exports=r;const o=n(5751);r.sep=o.sep;const i=Symbol("globstar **");r.GLOBSTAR=i;const a=n(5019),s={"!":{open:"(?:(?!(?:",close:"))[^/]*?)"},"?":{open:"(?:",close:")?"},"+":{open:"(?:",close:")+"},"*":{open:"(?:",close:")*"},"@":{open:"(?:",close:")"}},l="[^/]",c="[^/]*?",u=e=>e.split("").reduce(((e,t)=>(e[t]=!0,e)),{}),p=u("().*{}+?[]^$\\!"),d=u("[.("),f=/\/+/;r.filter=(e,t={})=>(n,o,i)=>r(n,e,t);const h=(e,t={})=>{const n={};return Object.keys(e).forEach((t=>n[t]=e[t])),Object.keys(t).forEach((e=>n[e]=t[e])),n};r.defaults=e=>{if(!e||"object"!=typeof e||!Object.keys(e).length)return r;const t=r,n=(n,r,o)=>t(n,r,h(e,o));return(n.Minimatch=class extends t.Minimatch{constructor(t,n){super(t,h(e,n))}}).defaults=n=>t.defaults(h(e,n)).Minimatch,n.filter=(n,r)=>t.filter(n,h(e,r)),n.defaults=n=>t.defaults(h(e,n)),n.makeRe=(n,r)=>t.makeRe(n,h(e,r)),n.braceExpand=(n,r)=>t.braceExpand(n,h(e,r)),n.match=(n,r,o)=>t.match(n,r,h(e,o)),n},r.braceExpand=(e,t)=>m(e,t);const m=(e,t={})=>(g(e),t.nobrace||!/\{(?:(?!\{).)*\}/.test(e)?[e]:a(e)),g=e=>{if("string"!=typeof e)throw new TypeError("invalid pattern");if(e.length>65536)throw new TypeError("pattern is too long")},y=Symbol("subparse");r.makeRe=(e,t)=>new v(e,t||{}).makeRe(),r.match=(e,t,n={})=>{const r=new v(t,n);return e=e.filter((e=>r.match(e))),r.options.nonull&&!e.length&&e.push(t),e};class v{constructor(e,t){g(e),t||(t={}),this.options=t,this.set=[],this.pattern=e,this.regexp=null,this.negate=!1,this.comment=!1,this.empty=!1,this.partial=!!t.partial,this.make()}debug(){}make(){const e=this.pattern,t=this.options;if(!t.nocomment&&"#"===e.charAt(0))return void(this.comment=!0);if(!e)return void(this.empty=!0);this.parseNegate();let n=this.globSet=this.braceExpand();t.debug&&(this.debug=(...e)=>console.error(...e)),this.debug(this.pattern,n),n=this.globParts=n.map((e=>e.split(f))),this.debug(this.pattern,n),n=n.map(((e,t,n)=>e.map(this.parse,this))),this.debug(this.pattern,n),n=n.filter((e=>-1===e.indexOf(!1))),this.debug(this.pattern,n),this.set=n}parseNegate(){if(this.options.nonegate)return;const e=this.pattern;let t=!1,n=0;for(let r=0;r>> no match, partial?",e,d,t,f),d!==s))}if("string"==typeof u?(c=p===u,this.debug("string match",u,p,c)):(c=p.match(u),this.debug("pattern match",u,p,c)),!c)return!1}if(o===s&&a===l)return!0;if(o===s)return n;if(a===l)return o===s-1&&""===e[o];throw new Error("wtf?")}braceExpand(){return m(this.pattern,this.options)}parse(e,t){g(e);const n=this.options;if("**"===e){if(!n.noglobstar)return i;e="*"}if(""===e)return"";let r="",o=!!n.nocase,a=!1;const u=[],f=[];let h,m,v,b,w=!1,x=-1,k=-1;const _="."===e.charAt(0)?"":n.dot?"(?!(?:^|\\/)\\.{1,2}(?:$|\\/))":"(?!\\.)",O=()=>{if(h){switch(h){case"*":r+=c,o=!0;break;case"?":r+=l,o=!0;break;default:r+="\\"+h}this.debug("clearStateChar %j %j",h,r),h=!1}};for(let t,i=0;i(n||(n="\\"),t+t+n+"|"))),this.debug("tail=%j\n %s",e,e,v,r);const t="*"===v.type?c:"?"===v.type?l:"\\"+v.type;o=!0,r=r.slice(0,v.reStart)+t+"\\("+e}O(),a&&(r+="\\\\");const S=d[r.charAt(0)];for(let e=f.length-1;e>-1;e--){const n=f[e],o=r.slice(0,n.reStart),i=r.slice(n.reStart,n.reEnd-8);let a=r.slice(n.reEnd);const s=r.slice(n.reEnd-8,n.reEnd)+a,l=o.split("(").length-1;let c=a;for(let e=0;e(e=e.map((e=>"string"==typeof e?e.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"):e===i?i:e._src)).reduce(((e,t)=>(e[e.length-1]===i&&t===i||e.push(t),e)),[]),e.forEach(((t,r)=>{t===i&&e[r-1]!==i&&(0===r?e.length>1?e[r+1]="(?:\\/|"+n+"\\/)?"+e[r+1]:e[r]=n:r===e.length-1?e[r-1]+="(?:\\/|"+n+")?":(e[r-1]+="(?:\\/|\\/"+n+"\\/)"+e[r+1],e[r+1]=i))})),e.filter((e=>e!==i)).join("/")))).join("|");o="^(?:"+o+")$",this.negate&&(o="^(?!"+o+").*$");try{this.regexp=new RegExp(o,r)}catch(e){this.regexp=!1}return this.regexp}match(e,t=this.partial){if(this.debug("match",e,this.pattern),this.comment)return!1;if(this.empty)return""===e;if("/"===e&&t)return!0;const n=this.options;"/"!==o.sep&&(e=e.split(o.sep).join("/")),e=e.split(f),this.debug(this.pattern,"split",e);const r=this.set;let i;this.debug(this.pattern,"set",r);for(let t=e.length-1;t>=0&&(i=e[t],!i);t--);for(let o=0;o=0&&c>0){if(e===t)return[l,c];for(r=[],i=n.length;u>=0&&!s;)u==l?(r.push(u),l=n.indexOf(e,u+1)):1==r.length?s=[r.pop(),c]:((o=r.pop())=0?l:c;r.length&&(s=[i,a])}return s}e.exports=t,t.range=r},4480:function(e,t,n){"use strict";var r=n.g.process&&process.nextTick||n.g.setImmediate||function(e){setTimeout(e,0)};e.exports=function(e,t){return e?void t.then((function(t){r((function(){e(null,t)}))}),(function(t){r((function(){e(t)}))})):t}},4184:function(e,t){var n;!function(){"use strict";var r={}.hasOwnProperty;function o(){for(var e=[],t=0;tu;)if((s=l[u++])!=s)return!0}else for(;c>u;u++)if((e||u in l)&&l[u]===n)return e||u||0;return!e&&-1}};e.exports={includes:a(!0),indexOf:a(!1)}},2092:function(e,t,n){var r=n(9974),o=n(8361),i=n(7908),a=n(7466),s=n(5417),l=[].push,c=function(e){var t=1==e,n=2==e,c=3==e,u=4==e,p=6==e,d=7==e,f=5==e||p;return function(h,m,g,y){for(var v,b,w=i(h),x=o(w),k=r(m,g,3),_=a(x.length),O=0,S=y||s,E=t?S(h,_):n||d?S(h,0):void 0;_>O;O++)if((f||O in x)&&(b=k(v=x[O],O,w),e))if(t)E[O]=b;else if(b)switch(e){case 3:return!0;case 5:return v;case 6:return O;case 2:l.call(E,v)}else switch(e){case 4:return!1;case 7:l.call(E,v)}return p?-1:c||u?u:E}};e.exports={forEach:c(0),map:c(1),filter:c(2),some:c(3),every:c(4),find:c(5),findIndex:c(6),filterOut:c(7)}},1194:function(e,t,n){var r=n(7293),o=n(5112),i=n(7392),a=o("species");e.exports=function(e){return i>=51||!r((function(){var t=[];return(t.constructor={})[a]=function(){return{foo:1}},1!==t[e](Boolean).foo}))}},5417:function(e,t,n){var r=n(111),o=n(3157),i=n(5112)("species");e.exports=function(e,t){var n;return o(e)&&("function"!=typeof(n=e.constructor)||n!==Array&&!o(n.prototype)?r(n)&&null===(n=n[i])&&(n=void 0):n=void 0),new(void 0===n?Array:n)(0===t?0:t)}},4326:function(e){var t={}.toString;e.exports=function(e){return t.call(e).slice(8,-1)}},648:function(e,t,n){var r=n(1694),o=n(4326),i=n(5112)("toStringTag"),a="Arguments"==o(function(){return arguments}());e.exports=r?o:function(e){var t,n,r;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(n=function(e,t){try{return e[t]}catch(e){}}(t=Object(e),i))?n:a?o(t):"Object"==(r=o(t))&&"function"==typeof t.callee?"Arguments":r}},9920:function(e,t,n){var r=n(6656),o=n(3887),i=n(1236),a=n(3070);e.exports=function(e,t){for(var n=o(t),s=a.f,l=i.f,c=0;c=74)&&(r=a.match(/Chrome\/(\d+)/))&&(o=r[1]),e.exports=o&&+o},748:function(e){e.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},2109:function(e,t,n){var r=n(7854),o=n(1236).f,i=n(8880),a=n(1320),s=n(3505),l=n(9920),c=n(4705);e.exports=function(e,t){var n,u,p,d,f,h=e.target,m=e.global,g=e.stat;if(n=m?r:g?r[h]||s(h,{}):(r[h]||{}).prototype)for(u in t){if(d=t[u],p=e.noTargetGet?(f=o(n,u))&&f.value:n[u],!c(m?u:h+(g?".":"#")+u,e.forced)&&void 0!==p){if(typeof d==typeof p)continue;l(d,p)}(e.sham||p&&p.sham)&&i(d,"sham",!0),a(n,u,d,e)}}},7293:function(e){e.exports=function(e){try{return!!e()}catch(e){return!0}}},9974:function(e,t,n){var r=n(3099);e.exports=function(e,t,n){if(r(e),void 0===t)return e;switch(n){case 0:return function(){return e.call(t)};case 1:return function(n){return e.call(t,n)};case 2:return function(n,r){return e.call(t,n,r)};case 3:return function(n,r,o){return e.call(t,n,r,o)}}return function(){return e.apply(t,arguments)}}},5005:function(e,t,n){var r=n(857),o=n(7854),i=function(e){return"function"==typeof e?e:void 0};e.exports=function(e,t){return arguments.length<2?i(r[e])||i(o[e]):r[e]&&r[e][t]||o[e]&&o[e][t]}},7854:function(e,t,n){var r=function(e){return e&&e.Math==Math&&e};e.exports=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof n.g&&n.g)||function(){return this}()||Function("return this")()},6656:function(e,t,n){var r=n(7908),o={}.hasOwnProperty;e.exports=Object.hasOwn||function(e,t){return o.call(r(e),t)}},3501:function(e){e.exports={}},490:function(e,t,n){var r=n(5005);e.exports=r("document","documentElement")},4664:function(e,t,n){var r=n(9781),o=n(7293),i=n(317);e.exports=!r&&!o((function(){return 7!=Object.defineProperty(i("div"),"a",{get:function(){return 7}}).a}))},8361:function(e,t,n){var r=n(7293),o=n(4326),i="".split;e.exports=r((function(){return!Object("z").propertyIsEnumerable(0)}))?function(e){return"String"==o(e)?i.call(e,""):Object(e)}:Object},2788:function(e,t,n){var r=n(5465),o=Function.toString;"function"!=typeof r.inspectSource&&(r.inspectSource=function(e){return o.call(e)}),e.exports=r.inspectSource},9909:function(e,t,n){var r,o,i,a=n(8536),s=n(7854),l=n(111),c=n(8880),u=n(6656),p=n(5465),d=n(6200),f=n(3501),h="Object already initialized",m=s.WeakMap;if(a||p.state){var g=p.state||(p.state=new m),y=g.get,v=g.has,b=g.set;r=function(e,t){if(v.call(g,e))throw new TypeError(h);return t.facade=e,b.call(g,e,t),t},o=function(e){return y.call(g,e)||{}},i=function(e){return v.call(g,e)}}else{var w=d("state");f[w]=!0,r=function(e,t){if(u(e,w))throw new TypeError(h);return t.facade=e,c(e,w,t),t},o=function(e){return u(e,w)?e[w]:{}},i=function(e){return u(e,w)}}e.exports={set:r,get:o,has:i,enforce:function(e){return i(e)?o(e):r(e,{})},getterFor:function(e){return function(t){var n;if(!l(t)||(n=o(t)).type!==e)throw TypeError("Incompatible receiver, "+e+" required");return n}}}},3157:function(e,t,n){var r=n(4326);e.exports=Array.isArray||function(e){return"Array"==r(e)}},4705:function(e,t,n){var r=n(7293),o=/#|\.prototype\./,i=function(e,t){var n=s[a(e)];return n==c||n!=l&&("function"==typeof t?r(t):!!t)},a=i.normalize=function(e){return String(e).replace(o,".").toLowerCase()},s=i.data={},l=i.NATIVE="N",c=i.POLYFILL="P";e.exports=i},111:function(e){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},1913:function(e){e.exports=!1},133:function(e,t,n){var r=n(7392),o=n(7293);e.exports=!!Object.getOwnPropertySymbols&&!o((function(){var e=Symbol();return!String(e)||!(Object(e)instanceof Symbol)||!Symbol.sham&&r&&r<41}))},8536:function(e,t,n){var r=n(7854),o=n(2788),i=r.WeakMap;e.exports="function"==typeof i&&/native code/.test(o(i))},30:function(e,t,n){var r,o=n(9670),i=n(6048),a=n(748),s=n(3501),l=n(490),c=n(317),u=n(6200)("IE_PROTO"),p=function(){},d=function(e){return"