From fa24d1f0819443f824d048ff6d0f661cf5320f12 Mon Sep 17 00:00:00 2001 From: shanshi Date: Thu, 7 Dec 2023 20:17:21 +0800 Subject: [PATCH] add features code answer and multi-agents by markdown --- .gitignore | 3 + Dockerfile | 2 + README.md | 42 +- README_en.md | 41 +- configs/__init__.py | 2 +- configs/model_config.py.example | 32 +- configs/server_config.py.example | 15 + dev_opsgpt/chat/__init__.py | 4 +- dev_opsgpt/chat/agent_chat.py | 171 +++- dev_opsgpt/chat/code_chat.py | 20 +- dev_opsgpt/chat/data_chat.py | 229 ------ dev_opsgpt/chat/tool_chat.py | 84 -- .../codebase_handler/codebase_handler.py | 139 ---- .../codedb_handler/local_codedb_handler.py | 55 -- .../networkx_handler/networkx_handler.py | 129 --- .../parser/java_paraser/__init__.py | 7 - .../parser/java_paraser/java_crawler.py | 32 - .../parser/java_paraser/java_dedup.py | 15 - .../parser/java_paraser/java_preprocess.py | 14 - .../codebase_handler/tagger/__init__.py | 7 - dev_opsgpt/codebase_handler/tagger/tagger.py | 48 -- .../tagger/tuple_generation.py | 51 -- .../codedb_handler => codechat}/__init__.py | 2 +- .../code_analyzer}/__init__.py | 2 +- .../codechat/code_analyzer/code_analyzer.py | 219 ++++++ .../codechat/code_analyzer/code_dedup.py | 31 + .../codechat/code_analyzer/code_intepreter.py | 229 ++++++ .../codechat/code_analyzer/code_preprocess.py | 14 + .../code_analyzer/code_static_analysis.py | 26 + .../language_static_analysis/__init__.py | 14 + .../java_static_analysis.py} | 45 +- dev_opsgpt/codechat/code_crawler/__init__.py | 15 + .../codechat/code_crawler/dir_crawler.py | 39 + .../codechat/code_crawler/zip_crawler.py | 31 + .../code_search}/__init__.py | 2 +- .../codechat/code_search/code_search.py | 179 +++++ .../codechat/code_search/cypher_generator.py | 63 ++ dev_opsgpt/codechat/code_search/tagger.py | 23 + .../codebase_handler}/__init__.py | 2 +- .../codebase_handler/code_importer.py | 175 +++++ .../codebase_handler/codebase_handler.py | 169 ++++ dev_opsgpt/connector/agents/__init__.py | 5 +- dev_opsgpt/connector/agents/base_agent.py | 732 ++++++++++++------ dev_opsgpt/connector/agents/check_agent.py | 110 +++ dev_opsgpt/connector/agents/executor_agent.py | 214 +++++ dev_opsgpt/connector/agents/react_agent.py | 115 ++- dev_opsgpt/connector/agents/selector_agent.py | 109 +++ dev_opsgpt/connector/chains/base_chain.py | 481 +++++++----- dev_opsgpt/connector/chains/chains.py | 28 +- dev_opsgpt/connector/configs/agent_config.py | 359 +++------ .../configs/agent_prompt/design_writer.yaml | 99 +++ .../configs/agent_prompt/prd_writer.yaml | 101 +++ .../configs/agent_prompt/review_code.yaml | 177 +++++ .../configs/agent_prompt/task_write.yaml | 148 ++++ .../configs/agent_prompt/write_code.yaml | 147 ++++ dev_opsgpt/connector/configs/chain_config.py | 107 ++- dev_opsgpt/connector/configs/phase_config.py | 37 +- .../connector/configs/prompts/__init__.py | 38 + .../prompts/checker_template_prompt.py | 37 + .../prompts/executor_template_prompt.py | 33 + .../configs/prompts/input_template_prompt.py | 40 + .../prompts/intention_template_prompt.py | 14 + .../configs/prompts/metagpt_prompt.py | 218 ++++++ .../prompts/planner_template_prompt.py | 114 +++ .../configs/prompts/qa_template_prompt.py | 52 ++ .../configs/prompts/react_code_prompt.py | 31 + .../configs/prompts/react_template_prompt.py | 31 + .../prompts/react_tool_code_planner_prompt.py | 49 ++ .../configs/prompts/react_tool_code_prompt.py | 81 ++ .../configs/prompts/react_tool_prompt.py | 81 ++ .../configs/prompts/refine_template_prompt.py | 30 + .../prompts/summary_template_prompt.py | 20 + dev_opsgpt/connector/message_process.py | 364 +++++++++ dev_opsgpt/connector/phase/base_phase.py | 198 ++--- dev_opsgpt/connector/schema/__init__.py | 9 + .../general_schema.py} | 128 +-- .../connector/{shcema => schema}/memory.py | 62 +- dev_opsgpt/connector/schema/message.py | 98 +++ dev_opsgpt/connector/shcema/__init__.py | 6 - dev_opsgpt/connector/utils.py | 25 + dev_opsgpt/db_handler/__init__.py | 7 + .../db_handler/graph_db_handler/__init__.py | 7 + .../graph_db_handler/nebula_handler.py | 263 +++++++ .../db_handler/vector_db_handler/__init__.py | 7 + .../vector_db_handler/chroma_handler.py | 140 ++++ dev_opsgpt/embeddings/get_embedding.py | 39 + .../embeddings/huggingface_embedding.py | 49 ++ dev_opsgpt/embeddings/openai_embedding.py | 50 ++ dev_opsgpt/embeddings/utils.py | 4 +- dev_opsgpt/orm/commands/code_base_cds.py | 15 +- dev_opsgpt/orm/schemas/base_schema.py | 4 +- dev_opsgpt/sandbox/pycodebox.py | 10 +- dev_opsgpt/service/cb_api.py | 37 +- dev_opsgpt/service/sdfile_api.py | 7 +- dev_opsgpt/tools/__init__.py | 37 +- dev_opsgpt/tools/cb_query_tool.py | 27 +- dev_opsgpt/tools/multiplier.py | 5 +- dev_opsgpt/tools/ocr_tool.py | 96 +++ dev_opsgpt/tools/stock_tool.py | 189 +++++ dev_opsgpt/utils/common_utils.py | 4 +- dev_opsgpt/utils/nebula_cp.sh | 3 + dev_opsgpt/utils/postprocess.py | 4 +- dev_opsgpt/utils/server_utils.py | 14 +- dev_opsgpt/webui/code.py | 6 + dev_opsgpt/webui/dialogue.py | 220 +++--- dev_opsgpt/webui/utils.py | 188 +++-- .../agent_examples/baseTaskPhase_example.py | 52 ++ .../agent_examples/codeChatPhase_example.py | 129 +++ .../agent_examples/codeReactPhase_example.py | 52 ++ .../coedToolReactPhase_example.py | 59 ++ .../agent_examples/docChatPhase_example.py | 67 ++ .../agent_examples/metagpt_phase_example.py | 41 + .../agent_examples/searchChatPhase_example.py | 67 ++ .../agent_examples/toolReactPhase_example.py | 53 ++ examples/docker_start.sh | 17 - examples/start.py | 35 +- examples/start_sandbox.py | 49 -- examples/start_service_docker.py | 68 -- examples/start_webui.sh | 12 - examples/stop.py | 1 - examples/stop_sandbox.py | 32 - examples/stop_webui.sh | 4 - requirements.txt | 3 + .../docs_imgs/devops-chatbot-module-v2.png | Bin 0 -> 665246 bytes sources/tool_datas/stock.json | 1 + tests/chains_test.py | 130 ++-- tests/docker_test.py | 12 +- tests/sandbox_test.py | 36 +- tests/tool_test.py | 7 + 129 files changed, 7102 insertions(+), 2407 deletions(-) delete mode 100644 dev_opsgpt/chat/data_chat.py delete mode 100644 dev_opsgpt/chat/tool_chat.py delete mode 100644 dev_opsgpt/codebase_handler/codebase_handler.py delete mode 100644 dev_opsgpt/codebase_handler/codedb_handler/local_codedb_handler.py delete mode 100644 dev_opsgpt/codebase_handler/networkx_handler/networkx_handler.py delete mode 100644 dev_opsgpt/codebase_handler/parser/java_paraser/__init__.py delete mode 100644 dev_opsgpt/codebase_handler/parser/java_paraser/java_crawler.py delete mode 100644 dev_opsgpt/codebase_handler/parser/java_paraser/java_dedup.py delete mode 100644 dev_opsgpt/codebase_handler/parser/java_paraser/java_preprocess.py delete mode 100644 dev_opsgpt/codebase_handler/tagger/__init__.py delete mode 100644 dev_opsgpt/codebase_handler/tagger/tagger.py delete mode 100644 dev_opsgpt/codebase_handler/tagger/tuple_generation.py rename dev_opsgpt/{codebase_handler/codedb_handler => codechat}/__init__.py (67%) rename dev_opsgpt/{codebase_handler/networkx_handler => codechat/code_analyzer}/__init__.py (67%) create mode 100644 dev_opsgpt/codechat/code_analyzer/code_analyzer.py create mode 100644 dev_opsgpt/codechat/code_analyzer/code_dedup.py create mode 100644 dev_opsgpt/codechat/code_analyzer/code_intepreter.py create mode 100644 dev_opsgpt/codechat/code_analyzer/code_preprocess.py create mode 100644 dev_opsgpt/codechat/code_analyzer/code_static_analysis.py create mode 100644 dev_opsgpt/codechat/code_analyzer/language_static_analysis/__init__.py rename dev_opsgpt/{codebase_handler/parser/java_paraser/java_parser.py => codechat/code_analyzer/language_static_analysis/java_static_analysis.py} (75%) create mode 100644 dev_opsgpt/codechat/code_crawler/__init__.py create mode 100644 dev_opsgpt/codechat/code_crawler/dir_crawler.py create mode 100644 dev_opsgpt/codechat/code_crawler/zip_crawler.py rename dev_opsgpt/{codebase_handler => codechat/code_search}/__init__.py (67%) create mode 100644 dev_opsgpt/codechat/code_search/code_search.py create mode 100644 dev_opsgpt/codechat/code_search/cypher_generator.py create mode 100644 dev_opsgpt/codechat/code_search/tagger.py rename dev_opsgpt/{codebase_handler/parser => codechat/codebase_handler}/__init__.py (67%) create mode 100644 dev_opsgpt/codechat/codebase_handler/code_importer.py create mode 100644 dev_opsgpt/codechat/codebase_handler/codebase_handler.py create mode 100644 dev_opsgpt/connector/agents/check_agent.py create mode 100644 dev_opsgpt/connector/agents/executor_agent.py create mode 100644 dev_opsgpt/connector/agents/selector_agent.py create mode 100644 dev_opsgpt/connector/configs/agent_prompt/design_writer.yaml create mode 100644 dev_opsgpt/connector/configs/agent_prompt/prd_writer.yaml create mode 100644 dev_opsgpt/connector/configs/agent_prompt/review_code.yaml create mode 100644 dev_opsgpt/connector/configs/agent_prompt/task_write.yaml create mode 100644 dev_opsgpt/connector/configs/agent_prompt/write_code.yaml create mode 100644 dev_opsgpt/connector/configs/prompts/__init__.py create mode 100644 dev_opsgpt/connector/configs/prompts/checker_template_prompt.py create mode 100644 dev_opsgpt/connector/configs/prompts/executor_template_prompt.py create mode 100644 dev_opsgpt/connector/configs/prompts/input_template_prompt.py create mode 100644 dev_opsgpt/connector/configs/prompts/intention_template_prompt.py create mode 100644 dev_opsgpt/connector/configs/prompts/metagpt_prompt.py create mode 100644 dev_opsgpt/connector/configs/prompts/planner_template_prompt.py create mode 100644 dev_opsgpt/connector/configs/prompts/qa_template_prompt.py create mode 100644 dev_opsgpt/connector/configs/prompts/react_code_prompt.py create mode 100644 dev_opsgpt/connector/configs/prompts/react_template_prompt.py create mode 100644 dev_opsgpt/connector/configs/prompts/react_tool_code_planner_prompt.py create mode 100644 dev_opsgpt/connector/configs/prompts/react_tool_code_prompt.py create mode 100644 dev_opsgpt/connector/configs/prompts/react_tool_prompt.py create mode 100644 dev_opsgpt/connector/configs/prompts/refine_template_prompt.py create mode 100644 dev_opsgpt/connector/configs/prompts/summary_template_prompt.py create mode 100644 dev_opsgpt/connector/message_process.py create mode 100644 dev_opsgpt/connector/schema/__init__.py rename dev_opsgpt/connector/{connector_schema.py => schema/general_schema.py} (58%) rename dev_opsgpt/connector/{shcema => schema}/memory.py (51%) create mode 100644 dev_opsgpt/connector/schema/message.py delete mode 100644 dev_opsgpt/connector/shcema/__init__.py create mode 100644 dev_opsgpt/db_handler/__init__.py create mode 100644 dev_opsgpt/db_handler/graph_db_handler/__init__.py create mode 100644 dev_opsgpt/db_handler/graph_db_handler/nebula_handler.py create mode 100644 dev_opsgpt/db_handler/vector_db_handler/__init__.py create mode 100644 dev_opsgpt/db_handler/vector_db_handler/chroma_handler.py create mode 100644 dev_opsgpt/embeddings/get_embedding.py create mode 100644 dev_opsgpt/embeddings/huggingface_embedding.py create mode 100644 dev_opsgpt/embeddings/openai_embedding.py create mode 100644 dev_opsgpt/tools/ocr_tool.py create mode 100644 dev_opsgpt/tools/stock_tool.py create mode 100644 dev_opsgpt/utils/nebula_cp.sh create mode 100644 examples/agent_examples/baseTaskPhase_example.py create mode 100644 examples/agent_examples/codeChatPhase_example.py create mode 100644 examples/agent_examples/codeReactPhase_example.py create mode 100644 examples/agent_examples/coedToolReactPhase_example.py create mode 100644 examples/agent_examples/docChatPhase_example.py create mode 100644 examples/agent_examples/metagpt_phase_example.py create mode 100644 examples/agent_examples/searchChatPhase_example.py create mode 100644 examples/agent_examples/toolReactPhase_example.py delete mode 100644 examples/docker_start.sh delete mode 100644 examples/start_sandbox.py delete mode 100644 examples/start_service_docker.py delete mode 100644 examples/start_webui.sh delete mode 100644 examples/stop_sandbox.py delete mode 100644 examples/stop_webui.sh create mode 100644 sources/docs_imgs/devops-chatbot-module-v2.png create mode 100644 sources/tool_datas/stock.json diff --git a/.gitignore b/.gitignore index 0152705..3848c44 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ embedding_models jupyter_work model_config.py server_config.py +internal_start.py code_base .DS_Store .idea +data +tests diff --git a/Dockerfile b/Dockerfile index 0ef02c7..f213f38 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,8 @@ RUN apt-get install -y iputils-ping telnetd net-tools vim tcpdump # RUN service inetutils-inetd start # service inetutils-inetd status +RUN wget https://oss-cdn.nebula-graph.com.cn/package/3.6.0/nebula-graph-3.6.0.ubuntu1804.amd64.deb +RUN dpkg -i nebula-graph-3.6.0.ubuntu1804.amd64.deb RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple RUN pip install -r /home/user/docker_requirements.txt diff --git a/README.md b/README.md index 4166303..8f2db38 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,11 @@

-本项目是一个开源的 AI 智能助手,专为软件开发的全生命周期而设计,涵盖设计、编码、测试、部署和运维等阶段。通过知识检索、代码检索,工具使用和沙箱执行,Codefuse-ChatBot 能解答您开发过程中的各种专业问题、问答操作周边独立分散平台。 +本项目是一个开源的 AI 智能助手,专为软件开发的全生命周期而设计,涵盖设计、编码、测试、部署和运维等阶段。通过知识检索、代码检索,工具使用和沙箱执行,Codefuse-ChatBot不仅能回答您在开发过程中遇到的专业问题,还能通过对话界面协调多个独立分散的平台。 ## 🔔 更新 +- [2023.12.01] Multi-Agent和代码库检索功能开放 - [2023.11.15] 增加基于本地代码库的问答增强模式 - [2023.09.15] 本地/隔离环境的沙盒功能开放,基于爬虫实现指定url知识检索 @@ -29,13 +30,12 @@ 💡 本项目旨在通过检索增强生成(Retrieval Augmented Generation,RAG)、工具学习(Tool Learning)和沙盒环境来构建软件开发全生命周期的AI智能助手,涵盖设计、编码、测试、部署和运维等阶段。 逐渐从各处资料查询、独立分散平台操作的传统开发运维模式转变到大模型问答的智能化开发运维模式,改变人们的开发运维习惯。 -- 📚 知识库管理:DevOps专业高质量知识库 + 企业级知识库自助构建 + 对话实现快速检索开源/私有技术文档 -- ⌨️ 代码知识库管理:支持本地代码库导入和代码结构解析 + 对话实现快速检索本地代码 -- 🐳 隔离沙盒环境:实现代码的快速编译执行测试 -- 🔄 React范式:支撑代码的自我迭代、自动执行 -- 🛠️ Prompt管理:实现各种开发、运维任务的prompt管理 -- 🔌 丰富的领域插件:执行各种定制开发任务 -- 🚀 对话驱动:需求设计、系分设计、代码生成、开发测试、部署运维自动化 +本项目核心差异技术、功能点: +- **🧠 智能调度核心:** 构建了体系链路完善的调度核心,支持多模式一键配置,简化操作流程。 +- **💻 代码整库分析:** 实现了仓库级的代码深入理解,以及项目文件级的代码编写与生成,提升了开发效率。 +- **📄 文档分析增强:** 融合了文档知识库与知识图谱,通过检索和推理增强,为文档分析提供了更深层次的支持。 +- **🔧 垂类专属知识:** 为DevOps领域定制的专属知识库,支持垂类知识库的自助一键构建,便捷实用。 +- **🤖 垂类模型兼容:** 针对DevOps领域的小型模型,保证了与DevOps相关平台的兼容性,促进了技术生态的整合。 🌍 依托于开源的 LLM 与 Embedding 模型,本项目可实现基于开源模型的离线私有部署。此外,本项目也支持 OpenAI API 的调用。 @@ -57,26 +57,26 @@ ## 🧭 技术路线
- 图片 + 图片
-- 🕷️ **Web Crawl**:实现定期网络文档爬取,确保数据的及时性,并依赖于开源社区的持续补充。 -- 🗂️ **DocLoader & TextSplitter**:对从多种来源爬取的数据进行数据清洗、去重和分类,并支持私有文档的导入。 -- 🗄️ **Vector Database**:结合Text Embedding模型对文档进行Embedding并在Milvus中存储。 -- 🔌 **Connector**:作为调度中心,负责LLM与Vector Database之间的交互调度,基于Langchain技术实现。 -- 📝 **Prompt Control**:从开发和运维角度设计,为不同问题分类并为Prompt添加背景,确保答案的可控性和完整性。 -- 💬 **LLM**:默认使用GPT-3.5-turbo,并为私有部署和其他涉及隐私的场景提供专有模型选择。 -- 🔤 **Text Embedding**:默认采用OpenAI的Text Embedding模型,支持私有部署和其他隐私相关场景,并提供专有模型选择。 -- 🚧 **SandBox**:对于生成的输出,如代码,为帮助用户判断其真实性,提供了一个交互验证环境(基于FaaS),并支持用户进行调整。 - +- 🧠 **Multi-Agent Schedule Core:** 多智能体调度核心,简易配置即可打造交互式智能体。 +- 🕷️ **Multi Source Web Crawl:** 多源网络爬虫,提供对指定 URL 的爬取功能,以搜集所需信息。 +- 🗂️ **Data Processor:** 数据处理器,轻松完成文档载入、数据清洗,及文本切分,整合不同来源的数据。 +- 🔤 **Text Embedding & Index:**:文本嵌入索引,用户可以轻松上传文件进行文档检索,优化文档分析过程。 +- 🗄️ **Vector Database & Graph Database:** 向量与图数据库,提供灵活强大的数据管理解决方案。 +- 📝 **Prompt Control & Management:**:Prompt 控制与管理,精确定义智能体的上下文环境。 +- 🚧 **SandBox:**:沙盒环境,安全地执行代码编译和动作。 +- 💬 **LLM:**:智能体大脑,支持多种开源模型和 LLM 接口。 +- 🛠️ **API Management::** API 管理工具,实现对开源组件和运维平台的快速集成。 具体实现明细见:[技术路线明细](sources/readme_docs/roadmap.md) -## 模型接入 +## 🌐 模型接入 -有需要接入的model,可以提issue +如果您需要集成特定的模型,请通过提交issue来告知我们您的需求。 | model_name | model_size | gpu_memory | quantize | HFhub | ModelScope | | ------------------ | ---------- | ---------- | -------- | ----- | ---------- | @@ -193,4 +193,4 @@ python start.py ## 🤗 致谢 -本项目基于[langchain-chatchat](https://github.com/chatchat-space/Langchain-Chatchat)和[codebox-api](https://github.com/shroominic/codebox-api),在此深深感谢他们的开源贡献! +本项目基于[langchain-chatchat](https://github.com/chatchat-space/Langchain-Chatchat)和[codebox-api](https://github.com/shroominic/codebox-api),在此深深感谢他们的开源贡献! \ No newline at end of file diff --git a/README_en.md b/README_en.md index 5a60a0f..ad7c0f6 100644 --- a/README_en.md +++ b/README_en.md @@ -9,16 +9,18 @@

-This project is an open-source AI intelligent assistant, specifically designed for the entire lifecycle of software development, covering design, coding, testing, deployment, and operations. Through knowledge retrieval, tool utilization, and sandbox execution, Codefuse-ChatBot can answer various professional questions during your development process and perform question-answering operations on standalone, disparate platforms. +This project is an open-source AI intelligent assistant, specifically designed for the entire lifecycle of software development, covering design, coding, testing, deployment, and operations. Through knowledge retrieval, tool utilization, and sandbox execution, Codefuse-ChatBot can not only answer professional questions you encounter during the development process but also coordinate multiple independent, dispersed platforms through a conversational interface. ## 🔔 Updates -- [2023.09.15] Sandbox features for local/isolated environments are now available, implementing specified URL knowledge retrieval based on web crawling. +- [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. +- [2023.09.15] Launch of sandbox functionality for local/isolated environments, enabling knowledge retrieval from specified URLs using web crawlers. ## 📜 Contents - [🤝 Introduction](#-introduction) - [🧭 Technical Route](#-technical-route) -- [🌐 模型接入](#-模型接入) +- [🌐 Model Integration](#-model-integration) - [🚀 Quick Start](#-quick-start) - [🤗 Acknowledgements](#-acknowledgements) @@ -26,11 +28,11 @@ 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. -- 📚 Knowledge Base Management: Professional high-quality Codefuse knowledge base + enterprise-level knowledge base self-construction + dialogue-based fast retrieval of open-source/private technical documents. -- 🐳 Isolated Sandbox Environment: Enables quick compilation, execution, and testing of code. -- 🔄 React Paradigm: Supports code self-iteration and automatic execution. -- 🛠️ Prompt Management: Manages prompts for various development and operations tasks. -- 🚀 Conversation Driven: Automates requirement design, system analysis design, code generation, development testing, deployment, and operations. +- **🧠 Intelligent Scheduling Core:** Constructed a well-integrated scheduling core system that supports multi-mode one-click configuration, simplifying the operational process. +- **💻 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. +- **🤖 Compatible Models for Specific Verticals:** Designed small models specifically for the DevOps field, ensuring compatibility with related DevOps platforms and promoting the integration of the technological ecosystem. 🌍 Relying on open-source LLM and Embedding models, this project can achieve offline private deployments based on open-source models. Additionally, this project also supports the use of the OpenAI API. @@ -47,24 +49,25 @@ This project is an open-source AI intelligent assistant, specifically designed f ## 🧭 Technical Route
- Image + Image
-- 🕷️ **Web Crawl**: Implements periodic web document crawling to ensure data timeliness and relies on continuous supplementation from the open-source community. -- 🗂️ **DocLoader & TextSplitter**: Cleans, deduplicates, and categorizes data crawled from various sources and supports the import of private documents. -- 🗄️ **Vector Database**: Integrates Text Embedding models to embed documents and store them in Milvus. -- 🔌 **Connector**: Acts as the scheduling center, responsible for coordinating interactions between LLM and Vector Database, implemented based on Langchain technology. -- 📝 **Prompt Control**: Designs from development and operations perspectives, categorizes different problems, and adds backgrounds to prompts to ensure the controllability and completeness of answers. -- 💬 **LLM**: Uses GPT-3.5-turbo by default and provides proprietary model options for private deployments and other privacy-related scenarios. -- 🔤 **Text Embedding**: Uses OpenAI's Text Embedding model by default, supports private deployments and other privacy-related scenarios, and provides proprietary model options. -- 🚧 **SandBox**: For generated outputs, like code, to help users judge their authenticity, an interactive verification environment is provided (based on FaaS), allowing user adjustments. +- 🧠 **Multi-Agent Schedule Core:** Easily configurable to create interactive intelligent agents. +- 🕷️ **Multi Source Web Crawl:** Offers the capability to crawl specified URLs for collecting the required information. +- 🗂️ **Data Processor:** Effortlessly handles document loading, data cleansing, and text segmentation, integrating data from different sources. +- 🔤 **Text Embedding & Index:**:Users can easily upload files for document retrieval, optimizing the document analysis process. +- 🗄️ **Vector Database & Graph Database:** Provides flexible and powerful data management solutions. +- 📝 **Prompt Control & Management:**:Precisely defines the contextual environment for intelligent agents. +- 🚧 **SandBox:**:Safely executes code compilation and actions. +- 💬 **LLM:**:Supports various open-source models and LLM interfaces. +- 🛠️ **API Management::** Enables rapid integration of open-source components and operational platforms. For implementation details, see: [Technical Route Details](sources/readme_docs/roadmap.md) -## 模型接入 +## 🌐 Model Integration -有需要接入的model,可以提issue +If you need to integrate a specific model, please inform us of your requirements by submitting an issue. | model_name | model_size | gpu_memory | quantize | HFhub | ModelScope | | ------------------ | ---------- | ---------- | -------- | ----- | ---------- | diff --git a/configs/__init__.py b/configs/__init__.py index dd01d3c..9898291 100644 --- a/configs/__init__.py +++ b/configs/__init__.py @@ -1,4 +1,4 @@ from .model_config import * from .server_config import * -VERSION = "v0.0.1" \ No newline at end of file +VERSION = "v0.1.0" \ No newline at end of file diff --git a/configs/model_config.py.example b/configs/model_config.py.example index 753bbc9..29b22ea 100644 --- a/configs/model_config.py.example +++ b/configs/model_config.py.example @@ -1,6 +1,10 @@ import os +import sys import logging import torch +import openai +import base64 +from .utils import is_running_in_docker # 日志格式 LOG_FORMAT = "%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s" logger = logging.getLogger() @@ -8,8 +12,11 @@ logger.setLevel(logging.INFO) logging.basicConfig(format=LOG_FORMAT) # os.environ["OPENAI_PROXY"] = "socks5h://127.0.0.1:13659" +os.environ["API_BASE_URL"] = "http://openai.com/v1/chat/completions" os.environ["OPENAI_API_KEY"] = "" os.environ["DUCKDUCKGO_PROXY"] = "socks5://127.0.0.1:13659" +os.environ["BAIDU_OCR_API_KEY"] = "" +os.environ["BAIDU_OCR_SECRET_KEY"] = "" import platform system_name = platform.system() @@ -38,6 +45,7 @@ LOCAL_MODEL_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(_ embedding_model_dict = {k: f"/home/user/chatbot/embedding_models/{v}" if is_running_in_docker() else f"{LOCAL_MODEL_DIR}/{v}" for k, v in embedding_model_dict.items()} # 选用的 Embedding 名称 +EMBEDDING_ENGINE = 'openai' EMBEDDING_MODEL = "text2vec-base" # Embedding 模型运行设备 @@ -99,6 +107,20 @@ llm_model_dict = { } +LOCAL_LLM_MODEL_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "llm_models") +llm_model_dict_c = {} +for k, v in llm_model_dict.items(): + v_c = {} + for kk, vv in v.items(): + if k=="local_model_path": + v_c[kk] = f"/home/user/chatbot/llm_models/{vv}" if is_running_in_docker() else f"{LOCAL_LLM_MODEL_DIR}/{vv}" + else: + v_c[kk] = vv + llm_model_dict_c[k] = v_c + +llm_model_dict = llm_model_dict_c + + # LLM 名称 LLM_MODEL = "gpt-3.5-turbo" USE_FASTCHAT = "gpt" not in LLM_MODEL # 判断是否进行fastchat @@ -129,7 +151,10 @@ JUPYTER_WORK_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath # WEB_CRAWL存储路径 WEB_CRAWL_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "sources/docs") -for _path in [LOG_PATH, SOURCE_PATH, KB_ROOT_PATH, NLTK_DATA_PATH, JUPYTER_WORK_PATH, WEB_CRAWL_PATH]: +# NEBULA_DATA存储路径 +NELUBA_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data/neluba_data") + +for _path in [LOG_PATH, SOURCE_PATH, KB_ROOT_PATH, NLTK_DATA_PATH, JUPYTER_WORK_PATH, WEB_CRAWL_PATH, NELUBA_PATH]: if not os.path.exists(_path): os.mkdir(_path) @@ -194,6 +219,11 @@ CODE_PROMPT_TEMPLATE = """【指令】根据已知信息来回答问题。 【问题】{question}""" +# 代码解释模版 +CODE_INTERPERT_TEMPLATE = '''{code} + +解释一下这段代码''' + # API 是否开启跨域,默认为False,如果需要开启,请设置为True # is open cross domain OPEN_CROSS_DOMAIN = False diff --git a/configs/server_config.py.example b/configs/server_config.py.example index b355dcd..8f3dd7a 100644 --- a/configs/server_config.py.example +++ b/configs/server_config.py.example @@ -45,6 +45,21 @@ FSCHAT_OPENAI_API = { "docker_port": 8888, # model_config.llm_model_dict中模型配置的api_base_url需要与这里一致。 } +# nebula conf +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 conf +CHROMA_PERSISTENT_PATH = '/home/user/chatbot/data/chroma_data' + # sandbox api server SANDBOX_CONTRAINER_NAME = "devopsgpt_sandbox" SANDBOX_IMAGE_NAME = "devopsgpt:py39" diff --git a/dev_opsgpt/chat/__init__.py b/dev_opsgpt/chat/__init__.py index 737d802..adb51e6 100644 --- a/dev_opsgpt/chat/__init__.py +++ b/dev_opsgpt/chat/__init__.py @@ -2,12 +2,10 @@ from .base_chat import Chat from .knowledge_chat import KnowledgeChat from .llm_chat import LLMChat from .search_chat import SearchChat -from .tool_chat import ToolChat -from .data_chat import DataChat from .code_chat import CodeChat from .agent_chat import AgentChat __all__ = [ - "Chat", "KnowledgeChat", "LLMChat", "SearchChat", "ToolChat", "DataChat", "CodeChat", "AgentChat" + "Chat", "KnowledgeChat", "LLMChat", "SearchChat", "CodeChat", "AgentChat" ] diff --git a/dev_opsgpt/chat/agent_chat.py b/dev_opsgpt/chat/agent_chat.py index 2ec4261..95ebc9e 100644 --- a/dev_opsgpt/chat/agent_chat.py +++ b/dev_opsgpt/chat/agent_chat.py @@ -1,10 +1,11 @@ from fastapi import Body, Request from fastapi.responses import StreamingResponse -from typing import List +from typing import List, Union, Dict from loguru import logger import importlib import copy import json +from pathlib import Path from configs.model_config import ( llm_model_dict, LLM_MODEL, PROMPT_TEMPLATE, @@ -18,12 +19,12 @@ from dev_opsgpt.tools import ( from dev_opsgpt.connector.phase import BasePhase from dev_opsgpt.connector.agents import BaseAgent, ReactAgent from dev_opsgpt.connector.chains import BaseChain -from dev_opsgpt.connector.connector_schema import ( +from dev_opsgpt.connector.schema import ( Message, load_phase_configs, load_chain_configs, load_role_configs ) -from dev_opsgpt.connector.shcema import Memory - +from dev_opsgpt.connector.schema import Memory +from dev_opsgpt.utils.common_utils import file_normalize from dev_opsgpt.chat.utils import History, wrap_done from dev_opsgpt.connector.configs import PHASE_CONFIGS, AGETN_CONFIGS, CHAIN_CONFIGS @@ -41,6 +42,7 @@ class AgentChat: ) -> None: self.top_k = top_k self.stream = stream + self.chatPhase_dict: Dict[str, BasePhase] = {} def chat( self, @@ -67,7 +69,8 @@ class AgentChat: custom_chain_configs: dict = Body({}, description="自定义chain配置"), custom_role_configs: dict = Body({}, description="自定义role配置"), history_node_list: List = Body([], description="代码历史相关节点"), - isDetaild: bool = Body([], description="是否输出完整的agent相关内容"), + isDetailed: bool = Body(False, description="是否输出完整的agent相关内容"), + upload_file: Union[str, Path, bytes] = "", **kargs ) -> Message: @@ -83,11 +86,21 @@ class AgentChat: # choose tools tools = toLangchainTools([TOOL_DICT[i] for i in choose_tools if i in TOOL_DICT]) + logger.debug(f"upload_file: {upload_file}") + + 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="human", role_name="user", input_query=query, + origin_query=query, phase_name=phase_name, chain_name=chain_name, do_search=do_search, @@ -101,7 +114,7 @@ class AgentChat: tools=tools ) # history memory mangemant - history = Memory([ + history = Memory(messages=[ Message(role_name=i["role"], role_type=i["role"], role_content=i["content"]) for i in history ]) @@ -128,14 +141,18 @@ class AgentChat: # "figures": output_message.figures # } - def chat_iterator(message: Message, local_memory: Memory, isDetaild=False): + 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 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 + "figures": message.figures, + "step_content": step_content, + "final_content": final_content, } @@ -146,8 +163,8 @@ class AgentChat: related_nodes.append(node) result["related_nodes"] = related_nodes - # logger.debug(f"{result['figures'].keys()}") - message_str = local_memory.to_str_messages(content_key='step_content') if isDetaild else message.role_content + # logger.debug(f"{result['figures'].keys()}, isDetailed: {isDetailed}") + message_str = step_content if self.stream: for token in message_str: result["answer"] = token @@ -157,7 +174,139 @@ class AgentChat: result["answer"] += token yield json.dumps(result, ensure_ascii=False) - return StreamingResponse(chat_iterator(output_message, local_memory, isDetaild), media_type="text/event-stream") + 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(VECTOR_SEARCH_TOP_K, description="匹配向量数"), + score_threshold: float = Body(SCORE_THRESHOLD, 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] = "", + **kargs + ) -> Message: + + # update configs + phase_configs, chain_configs, agent_configs = self.update_configs( + custom_phase_configs, custom_chain_configs, custom_role_configs) + # choose tools + tools = toLangchainTools([TOOL_DICT[i] for i in choose_tools if i in TOOL_DICT]) + logger.debug(f"upload_file: {upload_file}") + + 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="human", + role_name="user", + 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, + phase_config = phase_configs, + chain_config = chain_configs, + role_config = agent_configs, + do_summary=phase_configs[input_message.phase_name]["do_summary"], + do_code_retrieval=input_message.do_code_retrieval, + do_doc_retrieval=input_message.do_doc_retrieval, + do_search=input_message.do_search, + ) + 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=["user"]) + step_content = "\n\n".join([f"{v}" for parsed_output in local_memory.get_parserd_output_list() 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, + "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.role_content}") + # 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 diff --git a/dev_opsgpt/chat/code_chat.py b/dev_opsgpt/chat/code_chat.py index a7df657..9068bfb 100644 --- a/dev_opsgpt/chat/code_chat.py +++ b/dev_opsgpt/chat/code_chat.py @@ -53,23 +53,23 @@ class CodeChat(Chat): def _process(self, query: str, history: List[History], model): '''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) - codes = codes_res['related_code'] - nodes = codes_res['related_node'] + 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)) + # 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)) - context = "\n".join(codes) source_nodes = [] - for inum, node_info in enumerate(nodes[0:5]): - node_name, node_type, node_score = node_info[0], node_info[1], node_info[2] - source_nodes.append(f'{inum + 1}. 节点名为 {node_name}, 节点类型为 `{node_type}`, 节点得分为 `{node_score}`') + 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)])) @@ -90,6 +90,7 @@ class CodeChat(Chat): ), 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, @@ -100,6 +101,7 @@ class CodeChat(Chat): 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, **kargs) def _chat(self, query: str, history: List[History], **kargs): diff --git a/dev_opsgpt/chat/data_chat.py b/dev_opsgpt/chat/data_chat.py deleted file mode 100644 index 448aaf1..0000000 --- a/dev_opsgpt/chat/data_chat.py +++ /dev/null @@ -1,229 +0,0 @@ -import asyncio -from typing import List - -from langchain import LLMChain -from langchain.callbacks import AsyncIteratorCallbackHandler -from langchain.prompts.chat import ChatPromptTemplate -from langchain.agents import AgentType, initialize_agent - -from dev_opsgpt.tools import ( - WeatherInfo, WorldTimeGetTimezoneByArea, Multiplier, - toLangchainTools, get_tool_schema - ) -from .utils import History, wrap_done -from .base_chat import Chat -from loguru import logger -import json, re - -from dev_opsgpt.sandbox import PyCodeBox, CodeBoxResponse -from configs.server_config import SANDBOX_SERVER - -def get_tool_agent(tools, llm): - return initialize_agent( - tools, - llm, - agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, - verbose=True, - ) - -PROMPT_TEMPLATE = """ -`角色` -你是一个数据分析师,借鉴下述步骤,逐步完成数据分析任务的拆解和代码编写,尽可能帮助和准确地回答用户的问题。 - -数据文件的存放路径为 `./` - -`数据分析流程` -- 判断文件是否存在,并读取文件数据 -- 输出数据的基本信息,包括但不限于字段、文本、数据类型等 -- 输出数据的详细统计信息 -- 判断是否需要画图分析,选择合适的字段进行画图 -- 判断数据是否需要进行清洗 -- 判断数据或图片是否需要保存 -... -- 结合数据统计分析结果和画图结果,进行总结和分析这份数据的价值 - -`要求` -- 每轮选择一个数据分析流程,需要综合考虑上轮和后续的可能影响 -- 数据分析流程只提供参考,不要拘泥于它的具体流程,要有自己的思考 -- 使用JSON blob来指定一个计划,通过提供task_status关键字(任务状态)、plan关键字(数据分析计划)和code关键字(可执行代码)。 - -合法的 "task_status" 值: "finished" 表明当前用户问题已被准确回答 或者 "continued" 表明用户问题仍需要进一步分析 - -`$JSON_BLOB如下所示` -``` -{{ - "task_status": $TASK_STATUS, - "plan": $PLAN, - "code": ```python\n$CODE``` -}} -``` - -`跟随如下示例` -问题: 输入待回答的问题 -行动:$JSON_BLOB - -... (重复 行动 N 次,每次只生成一个行动) - -行动: -``` -{{ - "task_status": "finished", - "plan": 我已经可以回答用户问题了,最后回答用户的内容 -}} - -``` - -`数据分析,开始` - -问题:{query} -""" - - -PROMPT_TEMPLATE_2 = """ -`角色` -你是一个数据分析师,借鉴下述步骤,逐步完成数据分析任务的拆解和代码编写,尽可能帮助和准确地回答用户的问题。 - -数据文件的存放路径为 `./` - -`数据分析流程` -- 判断文件是否存在,并读取文件数据 -- 输出数据的基本信息,包括但不限于字段、文本、数据类型等 -- 输出数据的详细统计信息 -- 判断数据是否需要进行清洗 -- 判断是否需要画图分析,选择合适的字段进行画图 -- 判断清洗后数据或图片是否需要保存 -... -- 结合数据统计分析结果和画图结果,进行总结和分析这份数据的价值 - -`要求` -- 每轮选择一个数据分析流程,需要综合考虑上轮和后续的可能影响 -- 数据分析流程只提供参考,不要拘泥于它的具体流程,要有自己的思考 -- 使用JSON blob来指定一个计划,通过提供task_status关键字(任务状态)、plan关键字(数据分析计划)和code关键字(可执行代码)。 - -合法的 "task_status" 值: "finished" 表明当前用户问题已被准确回答 或者 "continued" 表明用户问题仍需要进一步分析 - -`$JSON_BLOB如下所示` -``` -{{ - "task_status": $TASK_STATUS, - "plan": $PLAN, - "code": ```python\n$CODE``` -}} -``` - -`跟随如下示例` -问题: 输入待回答的问题 -行动:$JSON_BLOB - -... (重复 行动 N 次,每次只生成一个行动) - -行动: -``` -{{ - "task_status": "finished", - "plan": 我已经可以回答用户问题了,最后回答用户的内容 -}} - -`数据分析,开始` - -问题:上传了一份employee_data.csv文件,请对它进行数据分析 - -问题:{query} -{history} - -""" - -class DataChat(Chat): - - def __init__( - self, - engine_name: str = "", - top_k: int = 1, - stream: bool = False, - ) -> None: - super().__init__(engine_name, top_k, stream) - self.tool_prompt = """结合上下文信息,{tools} {input}""" - self.codebox = PyCodeBox( - remote_url=SANDBOX_SERVER["url"], - remote_ip=SANDBOX_SERVER["host"], # "http://localhost", - remote_port=SANDBOX_SERVER["port"], - token="mytoken", - do_code_exe=True, - do_remote=SANDBOX_SERVER["do_remote"] - ) - - def create_task(self, query: str, history: List[History], model): - '''构建 llm 生成任务''' - logger.debug("content:{}".format([i.to_msg_tuple() for i in history] + [("human", PROMPT_TEMPLATE)])) - chat_prompt = ChatPromptTemplate.from_messages( - [i.to_msg_tuple() for i in history] + [("human", PROMPT_TEMPLATE)] - ) - pattern = re.compile(r"```(?:json)?\n(.*?)\n", re.DOTALL) - internal_history = [] - retry_nums = 2 - while retry_nums >= 0: - if len(internal_history) == 0: - chat_prompt = ChatPromptTemplate.from_messages( - [i.to_msg_tuple() for i in history] + [("human", PROMPT_TEMPLATE)] - ) - else: - chat_prompt = ChatPromptTemplate.from_messages( - [i.to_msg_tuple() for i in history] + [("human", PROMPT_TEMPLATE_2)] - ) - - chain = LLMChain(prompt=chat_prompt, llm=model) - content = chain({"query": query, "history": "\n".join(internal_history)})["text"] - - # content = pattern.search(content) - # logger.info(f"content: {content}") - # content = json.loads(content.group(1).strip(), strict=False) - - internal_history.append(f"{content}") - refer_info = "\n".join(internal_history) - logger.info(f"refer_info: {refer_info}") - try: - content = content.split("行动:")[-1].split("行动:")[-1] - content = json.loads(content) - except: - content = content.split("行动:")[-1].split("行动:")[-1] - content = eval(content) - - if "finished" == content["task_status"]: - break - elif "code" in content: - # elif "```code" in content or "```python" in content: - # code_text = self.codebox.decode_code_from_text(content) - code_text = content["code"] - codebox_res = self.codebox.chat("```"+code_text+"```", do_code_exe=True) - - if codebox_res is not None and codebox_res.code_exe_status != 200: - logger.warning(f"{codebox_res.code_exe_response}") - internal_history.append(f"观察: 根据这个报错信息 {codebox_res.code_exe_response},进行代码修复") - - if codebox_res is not None and codebox_res.code_exe_status == 200: - if codebox_res.code_exe_type == "image/png": - base_text = f"```\n{code_text}\n```\n\n" - img_html = "".format( - codebox_res.code_exe_response - ) - internal_history.append(f"观察: {img_html}") - # logger.info('```\n'+code_text+'\n```'+"\n\n"+'```\n'+codebox_res.code_exe_response+'\n```') - else: - internal_history.append(f"观察: {codebox_res.code_exe_response}") - # logger.info('```\n'+code_text+'\n```'+"\n\n"+'```\n'+codebox_res.code_exe_response+'\n```') - else: - internal_history.append(f"观察:下一步应该怎么做?") - retry_nums -= 1 - - - return {"answer": "", "docs": ""}, {"text": "\n".join(internal_history)} - - def create_atask(self, query, history, model, callback: AsyncIteratorCallbackHandler): - chat_prompt = ChatPromptTemplate.from_messages( - [i.to_msg_tuple() for i in history] + [("human", PROMPT_TEMPLATE)] - ) - 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/dev_opsgpt/chat/tool_chat.py b/dev_opsgpt/chat/tool_chat.py deleted file mode 100644 index ceb6a86..0000000 --- a/dev_opsgpt/chat/tool_chat.py +++ /dev/null @@ -1,84 +0,0 @@ -import asyncio -from typing import List - -from langchain import LLMChain -from langchain.callbacks import AsyncIteratorCallbackHandler -from langchain.prompts.chat import ChatPromptTemplate -from langchain.agents import AgentType, initialize_agent -import langchain -from langchain.schema import ( - AgentAction - ) - - -# langchain.debug = True - -from dev_opsgpt.tools import ( - TOOL_SETS, TOOL_DICT, - toLangchainTools, get_tool_schema - ) -from .utils import History, wrap_done -from .base_chat import Chat -from loguru import logger - - -def get_tool_agent(tools, llm): - return initialize_agent( - tools, - llm, - agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, - verbose=True, - return_intermediate_steps=True - ) - - -class ToolChat(Chat): - - def __init__( - self, - engine_name: str = "", - top_k: int = 1, - stream: bool = False, - ) -> None: - super().__init__(engine_name, top_k, stream) - self.tool_prompt = """结合上下文信息,{tools} {input}""" - self.tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) - - def create_task(self, query: str, history: List[History], model, **kargs): - '''构建 llm 生成任务''' - logger.debug("content:{}".format([i.to_msg_tuple() for i in history] + [("human", "{query}")])) - # chat_prompt = ChatPromptTemplate.from_messages( - # [i.to_msg_tuple() for i in history] + [("human", "{query}")] - # ) - tools = kargs.get("tool_sets", []) - tools = toLangchainTools([TOOL_DICT[i] for i in tools if i in TOOL_DICT]) - agent = get_tool_agent(tools if tools else self.tools, model) - content = agent(query) - - logger.debug(f"content: {content}") - - s = "" - if isinstance(content, str): - s = content - else: - for i in content["intermediate_steps"]: - for j in i: - if isinstance(j, AgentAction): - s += j.log + "\n" - else: - s += "Observation: " + str(j) + "\n" - - s += "final answer:" + content["output"] - # chain = LLMChain(prompt=chat_prompt, llm=model) - # content = chain({"tools": tools, "input": query}) - return {"answer": "", "docs": ""}, {"text": s} - - def create_atask(self, query, history, model, callback: AsyncIteratorCallbackHandler): - chat_prompt = ChatPromptTemplate.from_messages( - [i.to_msg_tuple() for i in history] + [("human", self.tool_prompt)] - ) - 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/dev_opsgpt/codebase_handler/codebase_handler.py b/dev_opsgpt/codebase_handler/codebase_handler.py deleted file mode 100644 index 82e9879..0000000 --- a/dev_opsgpt/codebase_handler/codebase_handler.py +++ /dev/null @@ -1,139 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: codebase_handler.py -@time: 2023/10/23 下午5:05 -@desc: -''' - -from loguru import logger -import time -import os - -from dev_opsgpt.codebase_handler.parser.java_paraser.java_crawler import JavaCrawler -from dev_opsgpt.codebase_handler.parser.java_paraser.java_preprocess import JavaPreprocessor -from dev_opsgpt.codebase_handler.parser.java_paraser.java_dedup import JavaDedup -from dev_opsgpt.codebase_handler.parser.java_paraser.java_parser import JavaParser -from dev_opsgpt.codebase_handler.tagger.tagger import Tagger -from dev_opsgpt.codebase_handler.tagger.tuple_generation import node_edge_update - -from dev_opsgpt.codebase_handler.networkx_handler.networkx_handler import NetworkxHandler -from dev_opsgpt.codebase_handler.codedb_handler.local_codedb_handler import LocalCodeDBHandler - - -class CodeBaseHandler(): - def __init__(self, code_name: str, code_path: str = '', cb_root_path: str = '', history_node_list: list = []): - self.nh = None - self.lcdh = None - self.code_name = code_name - self.code_path = code_path - - self.codebase_path = cb_root_path + os.sep + code_name - self.graph_path = self.codebase_path + os.sep + 'graph.pk' - self.codedb_path = self.codebase_path + os.sep + 'codedb.pk' - - self.tagger = Tagger() - self.history_node_list = history_node_list - - def import_code(self, do_save: bool=False, do_load: bool=False) -> bool: - ''' - import code to codeBase - @param code_path: - @param do_save: - @param do_load: - @return: True as success; False as failure - ''' - if do_load: - logger.info('start load from codebase_path') - load_graph_path = self.graph_path - load_codedb_path = self.codedb_path - - st = time.time() - self.nh = NetworkxHandler(graph_path=load_graph_path) - logger.info('generate graph success, rt={}'.format(time.time() - st)) - - st = time.time() - self.lcdh = LocalCodeDBHandler(db_path=load_codedb_path) - logger.info('generate codedb success, rt={}'.format(time.time() - st)) - else: - logger.info('start load from code_path') - st = time.time() - java_code_dict = JavaCrawler.local_java_file_crawler(self.code_path) - logger.info('crawl success, rt={}'.format(time.time() - st)) - - jp = JavaPreprocessor() - java_code_dict = jp.preprocess(java_code_dict) - - jd = JavaDedup() - java_code_dict = jd.dedup(java_code_dict) - - st = time.time() - j_parser = JavaParser() - parse_res = j_parser.parse(java_code_dict) - logger.info('parse success, rt={}'.format(time.time() - st)) - - st = time.time() - tagged_code = self.tagger.generate_tag(parse_res) - node_list, edge_list = node_edge_update(parse_res.values()) - logger.info('get node and edge success, rt={}'.format(time.time() - st)) - - st = time.time() - self.nh = NetworkxHandler(node_list=node_list, edge_list=edge_list) - logger.info('generate graph success, rt={}'.format(time.time() - st)) - - st = time.time() - self.lcdh = LocalCodeDBHandler(tagged_code) - logger.info('CodeDB load success, rt={}'.format(time.time() - st)) - - if do_save: - save_graph_path = self.graph_path - save_codedb_path = self.codedb_path - self.nh.save_graph(save_graph_path) - self.lcdh.save_db(save_codedb_path) - - def search_code(self, query: str, code_limit: int, history_node_list: list = []): - ''' - search code related to query - @param self: - @param query: - @return: - ''' - # get query tag - query_tag_list = self.tagger.generate_tag_query(query) - - related_node_score_list = self.nh.search_node_with_score(query_tag_list=query_tag_list, - history_node_list=history_node_list) - - score_dict = { - i[0]: i[1] - for i in related_node_score_list - } - related_node = [i[0] for i in related_node_score_list] - related_score = [i[1] for i in related_node_score_list] - - related_code, code_related_node = self.lcdh.search_by_multi_tag(related_node, lim=code_limit) - - related_node = [ - (node, self.nh.get_node_type(node), score_dict[node]) - for node in code_related_node - ] - - related_node.sort(key=lambda x: x[2], reverse=True) - - logger.info('related_node={}'.format(related_node)) - logger.info('related_code={}'.format(related_code)) - logger.info('num of code={}'.format(len(related_code))) - return related_code, related_node - - def refresh_history(self): - self.history_node_list = [] - - - - - - - - - - diff --git a/dev_opsgpt/codebase_handler/codedb_handler/local_codedb_handler.py b/dev_opsgpt/codebase_handler/codedb_handler/local_codedb_handler.py deleted file mode 100644 index e8fb54a..0000000 --- a/dev_opsgpt/codebase_handler/codedb_handler/local_codedb_handler.py +++ /dev/null @@ -1,55 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: local_codedb_handler.py -@time: 2023/10/23 下午5:05 -@desc: -''' -import pickle - - -class LocalCodeDBHandler: - def __init__(self, tagged_code: dict = {}, db_path: str = ''): - if db_path: - with open(db_path, 'rb') as f: - self.data = pickle.load(f) - else: - self.data = {} - for code, tag in tagged_code.items(): - self.data[code] = str(tag) - - def search_by_single_tag(self, tag, lim): - res = list() - for k, v in self.data.items(): - if tag in v and k not in res: - res.append(k) - - if len(res) > lim: - break - return res - - def search_by_multi_tag(self, tag_list, lim=3): - res = list() - res_related_node = [] - for tag in tag_list: - single_tag_res = self.search_by_single_tag(tag, lim) - for code in single_tag_res: - if code not in res: - res.append(code) - res_related_node.append(tag) - if len(res) >= lim: - break - - # reverse order so that most relevant one is close to the query - res = res[0:lim] - res.reverse() - - return res, res_related_node - - def save_db(self, save_path): - with open(save_path, 'wb') as f: - pickle.dump(self.data, f) - - def __len__(self): - return len(self.data) - diff --git a/dev_opsgpt/codebase_handler/networkx_handler/networkx_handler.py b/dev_opsgpt/codebase_handler/networkx_handler/networkx_handler.py deleted file mode 100644 index ffc16be..0000000 --- a/dev_opsgpt/codebase_handler/networkx_handler/networkx_handler.py +++ /dev/null @@ -1,129 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: networkx_handler.py -@time: 2023/10/23 下午5:02 -@desc: -''' - -import networkx as nx -from loguru import logger -import matplotlib.pyplot as plt -import pickle -from collections import defaultdict -import json - -QUERY_SCORE = 10 -HISTORY_SCORE = 5 -RATIO = 0.5 - - -class NetworkxHandler: - def __init__(self, graph_path: str = '', node_list: list = [], edge_list: list = []): - if graph_path: - self.graph_path = graph_path - with open(graph_path, 'r') as f: - self.G = nx.node_link_graph(json.load(f)) - else: - self.G = nx.DiGraph() - self.populate_graph(node_list, edge_list) - logger.debug( - 'number of nodes={}, number of edges={}'.format(self.G.number_of_nodes(), self.G.number_of_edges())) - - self.query_score = QUERY_SCORE - self.history_score = HISTORY_SCORE - self.ratio = RATIO - - def populate_graph(self, node_list, edge_list): - ''' - populate graph with node_list and edge_list - ''' - self.G.add_nodes_from(node_list) - for edge in edge_list: - self.G.add_edge(edge[0], edge[-1], relation=edge[1]) - - def draw_graph(self, save_path: str): - ''' - draw and save to save_path - ''' - sub = plt.subplot(111) - nx.draw(self.G, with_labels=True) - - plt.savefig(save_path) - - def search_node(self, query_tag_list: list, history_node_list: list = []): - ''' - search node by tag_list, search from history_tag neighbors first - > query_tag_list: tag from query - > history_node_list - ''' - node_list = set() - - # search from history_tag_list first, then all nodes - for tag in query_tag_list: - add = False - for history_node in history_node_list: - connect_node_list: list = self.G.adj[history_node] - connect_node_list.insert(0, history_node) - for connect_node in connect_node_list: - node_name_lim = len(connect_node) if '_' not in connect_node else connect_node.index('_') - node_name = connect_node[0:node_name_lim] - if tag.lower() in node_name.lower(): - node_list.add(connect_node) - add = True - if not add: - for node in self.G.nodes(): - if tag.lower() in node.lower(): - node_list.add(node) - return node_list - - def search_node_with_score(self, query_tag_list: list, history_node_list: list = []): - ''' - search node by tag_list, search from history_tag neighbors first - > query_tag_list: tag from query - > history_node_list - ''' - logger.info('query_tag_list={}, history_node_list={}'.format(query_tag_list, history_node_list)) - node_dict = defaultdict(lambda: 0) - - # loop over query_tag_list and add node: - for tag in query_tag_list: - for node in self.G.nodes: - if tag.lower() in node.lower(): - node_dict[node] += self.query_score - - # loop over history_node and add node score - for node in history_node_list: - node_dict[node] += self.history_score - - logger.info('temp_res={}'.format(node_dict)) - - # adj score broadcast - for node in node_dict: - adj_node_list = self.G.adj[node] - for adj_node in adj_node_list: - node_dict[node] += node_dict.get(adj_node, 0) * self.ratio - - # sort - node_list = [(node, node_score) for node, node_score in node_dict.items()] - node_list.sort(key=lambda x: x[1], reverse=True) - return node_list - - def save_graph(self, save_path: str): - to_save = nx.node_link_data(self.G) - with open(save_path, 'w') as f: - json.dump(to_save, f) - - def __len__(self): - return self.G.number_of_nodes() - - def get_node_type(self, node_name): - node_type = self.G.nodes[node_name]['type'] - return node_type - - def refresh_graph(self, ): - with open(self.graph_path, 'r') as f: - self.G = nx.node_link_graph(json.load(f)) - - - diff --git a/dev_opsgpt/codebase_handler/parser/java_paraser/__init__.py b/dev_opsgpt/codebase_handler/parser/java_paraser/__init__.py deleted file mode 100644 index ee1c9a5..0000000 --- a/dev_opsgpt/codebase_handler/parser/java_paraser/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: __init__.py.py -@time: 2023/10/23 下午5:01 -@desc: -''' \ No newline at end of file diff --git a/dev_opsgpt/codebase_handler/parser/java_paraser/java_crawler.py b/dev_opsgpt/codebase_handler/parser/java_paraser/java_crawler.py deleted file mode 100644 index 0681033..0000000 --- a/dev_opsgpt/codebase_handler/parser/java_paraser/java_crawler.py +++ /dev/null @@ -1,32 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: java_crawler.py -@time: 2023/10/23 下午5:02 -@desc: -''' - -import os -import glob -from loguru import logger - - -class JavaCrawler: - @staticmethod - def local_java_file_crawler(path: 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}*.java'.format(path=path, sep=os.path.sep), recursive=True) - java_code_dict = {} - - logger.debug('number of file={}'.format(len(java_file_list))) - # logger.debug(java_file_list) - - for java_file in java_file_list: - with open(java_file) as f: - java_code = ''.join(f.readlines()) - java_code_dict[java_file] = java_code - return java_code_dict \ No newline at end of file diff --git a/dev_opsgpt/codebase_handler/parser/java_paraser/java_dedup.py b/dev_opsgpt/codebase_handler/parser/java_paraser/java_dedup.py deleted file mode 100644 index c8e88b2..0000000 --- a/dev_opsgpt/codebase_handler/parser/java_paraser/java_dedup.py +++ /dev/null @@ -1,15 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: java_dedup.py -@time: 2023/10/23 下午5:02 -@desc: -''' - - -class JavaDedup: - def __init__(self): - pass - - def dedup(self, java_code_dict): - return java_code_dict \ No newline at end of file diff --git a/dev_opsgpt/codebase_handler/parser/java_paraser/java_preprocess.py b/dev_opsgpt/codebase_handler/parser/java_paraser/java_preprocess.py deleted file mode 100644 index c71729f..0000000 --- a/dev_opsgpt/codebase_handler/parser/java_paraser/java_preprocess.py +++ /dev/null @@ -1,14 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: java_preprocess.py -@time: 2023/10/23 下午5:04 -@desc: -''' - -class JavaPreprocessor: - def __init__(self): - pass - - def preprocess(self, java_code_dict): - return java_code_dict \ No newline at end of file diff --git a/dev_opsgpt/codebase_handler/tagger/__init__.py b/dev_opsgpt/codebase_handler/tagger/__init__.py deleted file mode 100644 index 05f385f..0000000 --- a/dev_opsgpt/codebase_handler/tagger/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: __init__.py.py -@time: 2023/10/23 下午5:00 -@desc: -''' \ No newline at end of file diff --git a/dev_opsgpt/codebase_handler/tagger/tagger.py b/dev_opsgpt/codebase_handler/tagger/tagger.py deleted file mode 100644 index 943c013..0000000 --- a/dev_opsgpt/codebase_handler/tagger/tagger.py +++ /dev/null @@ -1,48 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: tagger.py -@time: 2023/10/23 下午5:01 -@desc: -''' -import re -from loguru import logger - - -class Tagger: - def __init__(self): - pass - - def generate_tag(self, parse_res_dict: dict): - ''' - generate tag from parse_res - ''' - res = {} - for java_code, parse_res in parse_res_dict.items(): - tag = {} - tag['pac_name'] = parse_res.get('pac_name') - tag['class_name'] = set(parse_res.get('class_name_list')) - tag['func_name'] = set() - - for _, func_name_list in parse_res.get('func_name_dict', {}).items(): - tag['func_name'].update(func_name_list) - - res[java_code] = tag - return res - - 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)) - return tag_list - - -if __name__ == '__main__': - tagger = Tagger() - logger.debug(tagger.generate_tag_query('com.CheckHolder 有哪些函数')) - - - diff --git a/dev_opsgpt/codebase_handler/tagger/tuple_generation.py b/dev_opsgpt/codebase_handler/tagger/tuple_generation.py deleted file mode 100644 index fa33559..0000000 --- a/dev_opsgpt/codebase_handler/tagger/tuple_generation.py +++ /dev/null @@ -1,51 +0,0 @@ -# encoding: utf-8 -''' -@author: 温进 -@file: tuple_generation.py -@time: 2023/10/23 下午5:01 -@desc: -''' - - -def node_edge_update(parse_res_list: list, node_list: list = list(), edge_list: list = list()): - ''' - generate node and edge by parse_res - < node: list of string node - < edge: (node_st, relation, node_ed) - ''' - node_dict = {i: j for i, j in node_list} - - for single_parse_res in parse_res_list: - pac_name = single_parse_res['pac_name'] - - node_dict[pac_name] = {'type': 'package'} - - # class_name - for class_name in single_parse_res['class_name_list']: - node_dict[class_name] = {'type': 'class'} - edge_list.append((pac_name, 'contain', class_name)) - edge_list.append((class_name, 'inside', pac_name)) - - # func_name - for class_name, func_name_list in single_parse_res['func_name_dict'].items(): - node_list.append(class_name) - for func_name in func_name_list: - node_dict[func_name] = {'type': 'func'} - edge_list.append((class_name, 'contain', func_name)) - edge_list.append((func_name, 'inside', class_name)) - - # depend - for depend_pac_name in single_parse_res['import_pac_name_list']: - if depend_pac_name.endswith('*'): - depend_pac_name = depend_pac_name[0:-2] - - if depend_pac_name in node_dict: - continue - else: - node_dict[depend_pac_name] = {'type': 'unknown'} - edge_list.append((pac_name, 'depend', depend_pac_name)) - edge_list.append((depend_pac_name, 'beDepended', pac_name)) - - node_list = [(i, j) for i, j in node_dict.items()] - - return node_list, edge_list \ No newline at end of file diff --git a/dev_opsgpt/codebase_handler/codedb_handler/__init__.py b/dev_opsgpt/codechat/__init__.py similarity index 67% rename from dev_opsgpt/codebase_handler/codedb_handler/__init__.py rename to dev_opsgpt/codechat/__init__.py index 24ed759..35197bf 100644 --- a/dev_opsgpt/codebase_handler/codedb_handler/__init__.py +++ b/dev_opsgpt/codechat/__init__.py @@ -2,6 +2,6 @@ ''' @author: 温进 @file: __init__.py.py -@time: 2023/10/23 下午5:04 +@time: 2023/11/21 下午2:01 @desc: ''' \ No newline at end of file diff --git a/dev_opsgpt/codebase_handler/networkx_handler/__init__.py b/dev_opsgpt/codechat/code_analyzer/__init__.py similarity index 67% rename from dev_opsgpt/codebase_handler/networkx_handler/__init__.py rename to dev_opsgpt/codechat/code_analyzer/__init__.py index 05f385f..7704e55 100644 --- a/dev_opsgpt/codebase_handler/networkx_handler/__init__.py +++ b/dev_opsgpt/codechat/code_analyzer/__init__.py @@ -2,6 +2,6 @@ ''' @author: 温进 @file: __init__.py.py -@time: 2023/10/23 下午5:00 +@time: 2023/11/21 下午2:27 @desc: ''' \ No newline at end of file diff --git a/dev_opsgpt/codechat/code_analyzer/code_analyzer.py b/dev_opsgpt/codechat/code_analyzer/code_analyzer.py new file mode 100644 index 0000000..5375c61 --- /dev/null +++ b/dev_opsgpt/codechat/code_analyzer/code_analyzer.py @@ -0,0 +1,219 @@ +# encoding: utf-8 +''' +@author: 温进 +@file: code_analyzer.py +@time: 2023/11/21 下午2:27 +@desc: +''' +import time +from loguru import logger + +from dev_opsgpt.codechat.code_analyzer.code_static_analysis import CodeStaticAnalysis +from dev_opsgpt.codechat.code_analyzer.code_intepreter import CodeIntepreter +from dev_opsgpt.codechat.code_analyzer.code_preprocess import CodePreprocessor +from dev_opsgpt.codechat.code_analyzer.code_dedup import CodeDedup + + +class CodeAnalyzer: + def __init__(self, language: str): + self.code_preprocessor = CodePreprocessor() + self.code_debup = CodeDedup() + self.code_interperter = CodeIntepreter() + 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/dev_opsgpt/codechat/code_analyzer/code_dedup.py b/dev_opsgpt/codechat/code_analyzer/code_dedup.py new file mode 100644 index 0000000..071bd46 --- /dev/null +++ b/dev_opsgpt/codechat/code_analyzer/code_dedup.py @@ -0,0 +1,31 @@ +# 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/dev_opsgpt/codechat/code_analyzer/code_intepreter.py b/dev_opsgpt/codechat/code_analyzer/code_intepreter.py new file mode 100644 index 0000000..673bc44 --- /dev/null +++ b/dev_opsgpt/codechat/code_analyzer/code_intepreter.py @@ -0,0 +1,229 @@ +# 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 dev_opsgpt.llm_models.openai_model import getChatModel + + +class CodeIntepreter: + def __init__(self): + pass + + def get_intepretation(self, code_list): + ''' + get intepretion of code + @param code_list: + @return: + ''' + chat_model = getChatModel() + + 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() + + res = {} + messages = [] + for code in code_list: + message = CODE_INTERPERT_TEMPLATE.format(code=code) + messages.append(message) + + chat_ress = chat_model.batch(messages) + for chat_res, code in zip(chat_ress, code_list): + res[code] = chat_res.content + 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/dev_opsgpt/codechat/code_analyzer/code_preprocess.py b/dev_opsgpt/codechat/code_analyzer/code_preprocess.py new file mode 100644 index 0000000..50324ba --- /dev/null +++ b/dev_opsgpt/codechat/code_analyzer/code_preprocess.py @@ -0,0 +1,14 @@ +# 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/dev_opsgpt/codechat/code_analyzer/code_static_analysis.py b/dev_opsgpt/codechat/code_analyzer/code_static_analysis.py new file mode 100644 index 0000000..b943795 --- /dev/null +++ b/dev_opsgpt/codechat/code_analyzer/code_static_analysis.py @@ -0,0 +1,26 @@ +# encoding: utf-8 +''' +@author: 温进 +@file: code_static_analysis.py +@time: 2023/11/21 下午2:28 +@desc: +''' +from dev_opsgpt.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/dev_opsgpt/codechat/code_analyzer/language_static_analysis/__init__.py b/dev_opsgpt/codechat/code_analyzer/language_static_analysis/__init__.py new file mode 100644 index 0000000..c99e049 --- /dev/null +++ b/dev_opsgpt/codechat/code_analyzer/language_static_analysis/__init__.py @@ -0,0 +1,14 @@ +# 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/dev_opsgpt/codebase_handler/parser/java_paraser/java_parser.py b/dev_opsgpt/codechat/code_analyzer/language_static_analysis/java_static_analysis.py similarity index 75% rename from dev_opsgpt/codebase_handler/parser/java_paraser/java_parser.py rename to dev_opsgpt/codechat/code_analyzer/language_static_analysis/java_static_analysis.py index 23826d5..475da2d 100644 --- a/dev_opsgpt/codebase_handler/parser/java_paraser/java_parser.py +++ b/dev_opsgpt/codechat/code_analyzer/language_static_analysis/java_static_analysis.py @@ -1,26 +1,24 @@ # encoding: utf-8 ''' @author: 温进 -@file: java_parser.py -@time: 2023/10/23 下午5:03 +@file: java_static_analysis.py +@time: 2023/11/21 下午4:25 @desc: ''' -import json -import javalang -import glob import os from loguru import logger +import javalang -class JavaParser: +class JavaStaticAnalysis: def __init__(self): pass - def parse(self, java_code_list): + def analyze(self, java_code_dict): ''' parse java code and extract entity ''' - tree_dict = self.preparse(java_code_list) + tree_dict = self.preparse(java_code_dict) res = self.multi_java_code_parse(tree_dict) return res @@ -38,15 +36,15 @@ class JavaParser: continue if tree.package is not None: - tree_dict[java_code] = tree + 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): + def single_java_code_parse(self, tree, fp): ''' parse single code file > tree: javalang parse result - < {pac_name: '', class_name_list: [], func_name_list: [], import_pac_name_list: []]} + < {pac_name: '', class_name_list: [], func_name_dict: {}, import_pac_name_list: []]} ''' import_pac_name_list = [] @@ -57,25 +55,26 @@ class JavaParser: import_pac_name = import_pac.path import_pac_name_list.append(import_pac_name) - pac_name = tree.package.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 = pac_name + '.' + node.name + class_name = pac_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 + 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 + func_name = func_name + '-' + params_name if class_name not in func_name_dict: func_name_dict[class_name] = [] @@ -97,11 +96,21 @@ class JavaParser: < parse_result_dict ''' res_dict = {} - for java_code, tree in tree_dict.items(): + 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) + res_dict[java_code] = self.single_java_code_parse(tree, fp) except Exception as e: logger.debug(java_code) raise ImportError - return res_dict \ No newline at end of file + return res_dict + + + + + + + + diff --git a/dev_opsgpt/codechat/code_crawler/__init__.py b/dev_opsgpt/codechat/code_crawler/__init__.py new file mode 100644 index 0000000..6ddb8ef --- /dev/null +++ b/dev_opsgpt/codechat/code_crawler/__init__.py @@ -0,0 +1,15 @@ +# 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/dev_opsgpt/codechat/code_crawler/dir_crawler.py b/dev_opsgpt/codechat/code_crawler/dir_crawler.py new file mode 100644 index 0000000..2c27b84 --- /dev/null +++ b/dev_opsgpt/codechat/code_crawler/dir_crawler.py @@ -0,0 +1,39 @@ +# 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) 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/dev_opsgpt/codechat/code_crawler/zip_crawler.py b/dev_opsgpt/codechat/code_crawler/zip_crawler.py new file mode 100644 index 0000000..e5ed23a --- /dev/null +++ b/dev_opsgpt/codechat/code_crawler/zip_crawler.py @@ -0,0 +1,31 @@ +# encoding: utf-8 +''' +@author: 温进 +@file: zip_crawler.py +@time: 2023/11/21 下午2:02 +@desc: +''' +from loguru import logger + +import zipfile +from dev_opsgpt.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/dev_opsgpt/codebase_handler/__init__.py b/dev_opsgpt/codechat/code_search/__init__.py similarity index 67% rename from dev_opsgpt/codebase_handler/__init__.py rename to dev_opsgpt/codechat/code_search/__init__.py index ab840cf..c9e9dbe 100644 --- a/dev_opsgpt/codebase_handler/__init__.py +++ b/dev_opsgpt/codechat/code_search/__init__.py @@ -2,6 +2,6 @@ ''' @author: 温进 @file: __init__.py.py -@time: 2023/10/23 下午4:57 +@time: 2023/11/21 下午2:35 @desc: ''' \ No newline at end of file diff --git a/dev_opsgpt/codechat/code_search/code_search.py b/dev_opsgpt/codechat/code_search/code_search.py new file mode 100644 index 0000000..9a4c7ed --- /dev/null +++ b/dev_opsgpt/codechat/code_search/code_search.py @@ -0,0 +1,179 @@ +# encoding: utf-8 +''' +@author: 温进 +@file: code_search.py +@time: 2023/11/21 下午2:35 +@desc: +''' +import time +from loguru import logger +from collections import defaultdict + +from dev_opsgpt.db_handler.graph_db_handler.nebula_handler import NebulaHandler +from dev_opsgpt.db_handler.vector_db_handler.chroma_handler import ChromaHandler + +from dev_opsgpt.codechat.code_search.cypher_generator import CypherGenerator +from dev_opsgpt.codechat.code_search.tagger import Tagger +from dev_opsgpt.embeddings.get_embedding import get_embedding + +# search_by_tag +VERTEX_SCORE = 10 +HISTORY_VERTEX_SCORE = 5 +VERTEX_MERGE_RATIO = 0.5 + +# search_by_description +MAX_DISTANCE = 0.5 + + +class CodeSearch: + def __init__(self, nh: NebulaHandler, ch: ChromaHandler, limit: int = 3): + ''' + init + @param nh: NebulaHandler + @param ch: ChromaHandler + @param limit: limit of result + ''' + self.nh = nh + 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 verticex + vertex_list = self.nh.get_vertices().get('v', []) + vertex_vid_list = [i.as_node().get_id().as_string() for i in vertex_list] + logger.debug(vertex_vid_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.items(): + package = '#'.join(vertex.split('#')[0:2]) + 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] + 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 + return res + + def search_by_desciption(self, query: str, engine: str): + ''' + search by perform sim search + @param query: + @return: + ''' + query = query.replace(',', ',') + query_emb = get_embedding(engine=engine, text_list=[query]) + 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']) + logger.debug(query_result) + + 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() + 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 + + 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/dev_opsgpt/codechat/code_search/cypher_generator.py b/dev_opsgpt/codechat/code_search/cypher_generator.py new file mode 100644 index 0000000..16f52e4 --- /dev/null +++ b/dev_opsgpt/codechat/code_search/cypher_generator.py @@ -0,0 +1,63 @@ +# encoding: utf-8 +''' +@author: 温进 +@file: cypher_generator.py +@time: 2023/11/24 上午10:17 +@desc: +''' +from loguru import logger + +from dev_opsgpt.llm_models.openai_model import getChatModel +from dev_opsgpt.utils.postprocess import replace_lt_gt +from langchain.schema import ( + HumanMessage, +) +from langchain.chains.graph_qa.prompts import NGQL_GENERATION_PROMPT + + +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): + self.model = getChatModel() + + def get_cypher(self, query: str): + ''' + get cypher from query + @param query: + @return: + ''' + content = NGQL_GENERATION_PROMPT.format(schema=schema, question=query) + + 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 = '代码中一共有多少个类' + cg = CypherGenerator(engine='openai') + + cg.get_cypher(query) diff --git a/dev_opsgpt/codechat/code_search/tagger.py b/dev_opsgpt/codechat/code_search/tagger.py new file mode 100644 index 0000000..f7efa72 --- /dev/null +++ b/dev_opsgpt/codechat/code_search/tagger.py @@ -0,0 +1,23 @@ +# 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)) + return tag_list diff --git a/dev_opsgpt/codebase_handler/parser/__init__.py b/dev_opsgpt/codechat/codebase_handler/__init__.py similarity index 67% rename from dev_opsgpt/codebase_handler/parser/__init__.py rename to dev_opsgpt/codechat/codebase_handler/__init__.py index 05f385f..bcd2f89 100644 --- a/dev_opsgpt/codebase_handler/parser/__init__.py +++ b/dev_opsgpt/codechat/codebase_handler/__init__.py @@ -2,6 +2,6 @@ ''' @author: 温进 @file: __init__.py.py -@time: 2023/10/23 下午5:00 +@time: 2023/11/21 下午2:07 @desc: ''' \ No newline at end of file diff --git a/dev_opsgpt/codechat/codebase_handler/code_importer.py b/dev_opsgpt/codechat/codebase_handler/code_importer.py new file mode 100644 index 0000000..bf50b28 --- /dev/null +++ b/dev_opsgpt/codechat/codebase_handler/code_importer.py @@ -0,0 +1,175 @@ +# encoding: utf-8 +''' +@author: 温进 +@file: codebase_handler.py +@time: 2023/11/21 下午2:07 +@desc: +''' +import time +from loguru import logger + +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 dev_opsgpt.db_handler.graph_db_handler.nebula_handler import NebulaHandler +from dev_opsgpt.db_handler.vector_db_handler.chroma_handler import ChromaHandler +from dev_opsgpt.embeddings.get_embedding import get_embedding + + +class CodeImporter: + def __init__(self, codebase_name: str, engine: str, nh: NebulaHandler, ch: ChromaHandler): + self.codebase_name = codebase_name + self.engine = engine + self.nh = nh + self.ch = ch + + def import_code(self, static_analysis_res: dict, interpretation: dict, do_interpret: bool = True): + ''' + import code to nebula and chroma + @return: + ''' + self.analysis_res_to_graph(static_analysis_res) + self.interpretation_to_db(static_analysis_res, interpretation, do_interpret) + + 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.engine, text_list=interp_list) + 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(): + 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/dev_opsgpt/codechat/codebase_handler/codebase_handler.py b/dev_opsgpt/codechat/codebase_handler/codebase_handler.py new file mode 100644 index 0000000..66970c5 --- /dev/null +++ b/dev_opsgpt/codechat/codebase_handler/codebase_handler.py @@ -0,0 +1,169 @@ +# encoding: utf-8 +''' +@author: 温进 +@file: codebase_handler.py +@time: 2023/11/21 下午2:25 +@desc: +''' +import time +from loguru import logger + +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 configs.model_config import EMBEDDING_ENGINE + +from dev_opsgpt.db_handler.graph_db_handler.nebula_handler import NebulaHandler +from dev_opsgpt.db_handler.vector_db_handler.chroma_handler import ChromaHandler +from dev_opsgpt.codechat.code_crawler.zip_crawler import * +from dev_opsgpt.codechat.code_analyzer.code_analyzer import CodeAnalyzer +from dev_opsgpt.codechat.codebase_handler.code_importer import CodeImporter +from dev_opsgpt.codechat.code_search.code_search import CodeSearch + + +class CodeBaseHandler: + def __init__(self, codebase_name: str, code_path: str = '', + language: str = 'java', crawl_type: str = 'ZIP'): + self.codebase_name = codebase_name + self.code_path = code_path + self.language = language + self.crawl_type = crawl_type + + 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) + + 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(engine=EMBEDDING_ENGINE, codebase_name=self.codebase_name, + nh=self.nh, ch=self.ch) + 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) + 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 + stat = self.nh.get_stat() + vertices_num, edges_num = stat['vertices'], stat['edges'] + + # 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: + ''' + self.nh.drop_space(space_name=codebase_name) + 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: + ''' + assert search_type in ['cypher', 'tag', 'description'] + + code_search = CodeSearch(nh=self.nh, ch=self.ch, limit=limit) + + 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=EMBEDDING_ENGINE) + + 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 == 'description': + context = '' + related_vertice = [] + for code in search_res: + context = context + code['code_text'] + '\n' + related_vertice.append(code['vertex']) + + return context, related_vertice + + +if __name__ == '__main__': + codebase_name = 'testing' + code_path = '/Users/bingxu/Desktop/工作/大模型/chatbot/test_code_repo/client' + cbh = CodeBaseHandler(codebase_name, code_path, crawl_type='dir') + + # query = '使用不同的HTTP请求类型(GET、POST、DELETE等)来执行不同的操作' + # query = '代码中一共有多少个类' + + query = 'intercept 函数作用是什么' + search_type = 'graph' + limit = 2 + res = cbh.search_code(query, search_type, limit) + logger.debug(res) diff --git a/dev_opsgpt/connector/agents/__init__.py b/dev_opsgpt/connector/agents/__init__.py index b4aed3f..e0ee881 100644 --- a/dev_opsgpt/connector/agents/__init__.py +++ b/dev_opsgpt/connector/agents/__init__.py @@ -1,6 +1,9 @@ from .base_agent import BaseAgent from .react_agent import ReactAgent +from .check_agent import CheckAgent +from .executor_agent import ExecutorAgent +from .selector_agent import SelectorAgent __all__ = [ - "BaseAgent", "ReactAgent" + "BaseAgent", "ReactAgent", "CheckAgent", "ExecutorAgent", "SelectorAgent" ] \ No newline at end of file diff --git a/dev_opsgpt/connector/agents/base_agent.py b/dev_opsgpt/connector/agents/base_agent.py index 1def69c..f98724b 100644 --- a/dev_opsgpt/connector/agents/base_agent.py +++ b/dev_opsgpt/connector/agents/base_agent.py @@ -7,16 +7,19 @@ import traceback import uuid from loguru import logger -from dev_opsgpt.connector.shcema.memory import Memory -from dev_opsgpt.connector.connector_schema import ( - Task, Role, Message, ActionStatus, Doc, CodeDoc - ) +from dev_opsgpt.connector.schema import ( + Memory, Task, Env, Role, Message, ActionStatus, CodeDoc, Doc +) from configs.server_config import SANDBOX_SERVER from dev_opsgpt.sandbox import PyCodeBox, CodeBoxResponse from dev_opsgpt.tools import DDGSTool, DocRetrieval, CodeRetrieval -from dev_opsgpt.connector.configs.agent_config import REACT_PROMPT_INPUT +from dev_opsgpt.connector.configs.prompts import BASE_PROMPT_INPUT, QUERY_CONTEXT_DOC_PROMPT_INPUT, BEGIN_PROMPT_INPUT +from dev_opsgpt.connector.message_process import MessageUtils +from dev_opsgpt.connector.configs.agent_config import REACT_PROMPT_INPUT, QUERY_CONTEXT_PROMPT_INPUT, PLAN_PROMPT_INPUT from dev_opsgpt.llm_models import getChatModel +from dev_opsgpt.connector.utils import parse_section + class BaseAgent: @@ -34,67 +37,74 @@ class BaseAgent: stop: Union[List[str], str] = None, do_filter: bool = True, do_use_self_memory: bool = True, - # docs_prompt: str, + focus_agents: List[str] = [], + focus_message_keys: List[str] = [], # prompt_mamnger: PromptManager ): self.task = task self.role = role + self.message_utils = MessageUtils() self.llm = self.create_llm_engine(temperature, stop) self.memory = self.init_history(memory) self.chat_turn = chat_turn self.do_search = do_search self.do_doc_retrieval = do_doc_retrieval self.do_tool_retrieval = do_tool_retrieval - self.codebox = PyCodeBox( - remote_url=SANDBOX_SERVER["url"], - remote_ip=SANDBOX_SERVER["host"], - remote_port=SANDBOX_SERVER["port"], - token="mytoken", - do_code_exe=True, - do_remote=SANDBOX_SERVER["do_remote"], - do_check_net=False - ) + self.focus_agents = focus_agents + self.focus_message_keys = focus_message_keys self.do_filter = do_filter self.do_use_self_memory = do_use_self_memory - # self.docs_prompt = docs_prompt # self.prompt_manager = None - def run(self, query: Message, history: Memory = None, background: Memory = None) -> Message: - '''llm inference''' + def run(self, query: Message, history: Memory = None, background: Memory = None, memory_pool: Memory=None) -> Message: + '''agent reponse from multi-message''' + message = None + for message in self.arun(query, history, background, memory_pool): + pass + return message + + def arun(self, query: Message, history: Memory = None, background: Memory = None, memory_pool: Memory=None) -> Message: + '''agent reponse from multi-message''' # insert query into memory query_c = copy.deepcopy(query) self_memory = self.memory if self.do_use_self_memory else None - prompt = self.create_prompt(query_c, self_memory, history, background) + # create your llm prompt + prompt = self.create_prompt(query_c, self_memory, history, background, memory_pool=memory_pool) content = self.llm.predict(prompt) logger.debug(f"{self.role.role_name} prompt: {prompt}") - # logger.debug(f"{self.role.role_name} content: {content}") + logger.debug(f"{self.role.role_name} content: {content}") output_message = Message( role_name=self.role.role_name, role_type="ai", #self.role.role_type, role_content=content, role_contents=[content], + step_content=content, input_query=query_c.input_query, - tools=query_c.tools + tools=query_c.tools, + parsed_output_list=[query.parsed_output] ) - - output_message = self.parser(output_message) + # common parse llm' content to message + output_message = self.message_utils.parser(output_message) if self.do_filter: - output_message = self.filter(output_message) + output_message = self.message_utils.filter(output_message) - - # 更新自身的回答 + # update self_memory self.append_history(query_c) self.append_history(output_message) - logger.info(f"{self.role.role_name} step_run: {output_message.role_content}") - return output_message + # logger.info(f"{self.role.role_name} currenct question: {output_message.input_query}\nllm_step_run: {output_message.role_content}") + output_message.input_query = output_message.role_content + output_message.parsed_output_list.append(output_message.parsed_output) + # update memory pool + memory_pool.append(output_message) + yield output_message def create_prompt( - self, query: Message, memory: Memory =None, history: Memory = None, background: Memory = None, prompt_mamnger=None) -> str: + self, query: Message, memory: Memory =None, history: Memory = None, background: Memory = None, memory_pool: Memory=None, prompt_mamnger=None) -> str: ''' - role\task\tools\docs\memory + prompt engineer, contains role\task\tools\docs\memory ''' # doc_infos = self.create_doc_prompt(query) @@ -105,50 +115,105 @@ class BaseAgent: background_prompt = self.create_background_prompt(background, control_key="step_content") history_prompt = self.create_history_prompt(history) selfmemory_prompt = self.create_selfmemory_prompt(memory, control_key="step_content") - # + # extra_system_prompt = self.role.role_prompt + prompt = self.role.role_prompt.format(**{"formatted_tools": formatted_tools, "tool_names": tool_names}) + # + memory_pool_select_by_agent_key = self.select_memory_by_agent_key(memory_pool) + memory_pool_select_by_agent_key_context = '\n\n'.join([f"*{k}*\n{v}" for parsed_output in memory_pool_select_by_agent_key.get_parserd_output_list() for k, v in parsed_output.items() if k not in ['Action Status']]) + + # input_query = query.input_query + + # # logger.debug(f"{self.role.role_name} extra_system_prompt: {self.role.role_prompt}") + # # logger.debug(f"{self.role.role_name} input_query: {input_query}") + # # logger.debug(f"{self.role.role_name} doc_infos: {doc_infos}") + # # logger.debug(f"{self.role.role_name} tool_names: {tool_names}") + # if "**Context:**" in self.role.role_prompt: + # # logger.debug(f"parsed_output_list: {query.parsed_output_list}") + # # input_query = "'''" + "\n".join([f"###{k}###\n{v}" for i in query.parsed_output_list for k,v in i.items() if "Action Status" !=k]) + "'''" + # context = "\n".join([f"*{k}*\n{v}" for i in query.parsed_output_list for k,v in i.items() if "Action Status" !=k]) + # # context = history_prompt or '""' + # # logger.debug(f"parsed_output_list: {t}") + # prompt += "\n" + QUERY_CONTEXT_PROMPT_INPUT.format(**{"context": context, "query": query.origin_query}) + # else: + # prompt += "\n" + PLAN_PROMPT_INPUT.format(**{"query": input_query}) task = query.task or self.task if task_prompt is not None: prompt += "\n" + task.task_prompt + DocInfos = "" if doc_infos is not None and doc_infos!="" and doc_infos!="不存在知识库辅助信息": - prompt += f"\n知识库信息: {doc_infos}" + DocInfos += f"\nDocument Information: {doc_infos}" if code_infos is not None and code_infos!="" and code_infos!="不存在代码库辅助信息": - prompt += f"\n代码库信息: {code_infos}" + DocInfos += f"\nCodeBase Infomation: {code_infos}" + + # if selfmemory_prompt: + # prompt += "\n" + selfmemory_prompt - if background_prompt: - prompt += "\n" + background_prompt + # if background_prompt: + # prompt += "\n" + background_prompt - if history_prompt: - prompt += "\n" + history_prompt + # if history_prompt: + # prompt += "\n" + history_prompt - if selfmemory_prompt: - prompt += "\n" + selfmemory_prompt - - # input_query = memory.to_tuple_messages(content_key="step_content") - # input_query = "\n".join([f"{k}: {v}" for k, v in input_query if v]) - - input_query = query.role_content + input_query = query.input_query # logger.debug(f"{self.role.role_name} extra_system_prompt: {self.role.role_prompt}") - logger.debug(f"{self.role.role_name} input_query: {input_query}") + # logger.debug(f"{self.role.role_name} input_query: {input_query}") # logger.debug(f"{self.role.role_name} doc_infos: {doc_infos}") - logger.debug(f"{self.role.role_name} tool_names: {tool_names}") - prompt += "\n" + REACT_PROMPT_INPUT.format(**{"query": input_query}) + # logger.debug(f"{self.role.role_name} tool_names: {tool_names}") + + # extra_system_prompt = self.role.role_prompt + input_keys = parse_section(self.role.role_prompt, 'Input Format') + prompt = self.role.role_prompt.format(**{"formatted_tools": formatted_tools, "tool_names": tool_names}) + prompt += "\n" + BEGIN_PROMPT_INPUT + for input_key in input_keys: + if input_key == "Origin Query": + prompt += "\n**Origin Query:**\n" + query.origin_query + elif input_key == "Context": + context = "\n".join([f"*{k}*\n{v}" for i in query.parsed_output_list for k,v in i.items() if "Action Status" !=k]) + if history: + context = history_prompt + "\n" + context + if not context: + context = "there is no context" + + if self.focus_agents and memory_pool_select_by_agent_key_context: + context = memory_pool_select_by_agent_key_context + prompt += "\n**Context:**\n" + context + "\n" + input_query + elif input_key == "DocInfos": + prompt += "\n**DocInfos:**\n" + DocInfos + elif input_key == "Question": + prompt += "\n**Question:**\n" + input_query + + # if "**Context:**" in self.role.role_prompt: + # # logger.debug(f"parsed_output_list: {query.parsed_output_list}") + # # input_query = "'''" + "\n".join([f"###{k}###\n{v}" for i in query.parsed_output_list for k,v in i.items() if "Action Status" !=k]) + "'''" + # context = "\n".join([f"*{k}*\n{v}" for i in query.parsed_output_list for k,v in i.items() if "Action Status" !=k]) + # if history: + # context = history_prompt + "\n" + context + + # if not context: + # context = "there is no context" + + # # logger.debug(f"parsed_output_list: {t}") + # if "DocInfos" in prompt: + # prompt += "\n" + QUERY_CONTEXT_DOC_PROMPT_INPUT.format(**{"context": context, "query": query.origin_query, "DocInfos": DocInfos}) + # else: + # prompt += "\n" + QUERY_CONTEXT_PROMPT_INPUT.format(**{"context": context, "query": query.origin_query, "DocInfos": DocInfos}) + # else: + # prompt += "\n" + BASE_PROMPT_INPUT.format(**{"query": input_query}) + # prompt = extra_system_prompt.format(**{"query": input_query, "doc_infos": doc_infos, "formatted_tools": formatted_tools, "tool_names": tool_names}) while "{{" in prompt or "}}" in prompt: prompt = prompt.replace("{{", "{") prompt = prompt.replace("}}", "}") + + # logger.debug(f"{self.role.role_name} prompt: {prompt}") return prompt - - # prompt_comp = [("system", extra_system_prompt)] + memory.to_tuple_messages() - # prompt = ChatPromptTemplate.from_messages(prompt_comp) - # prompt = prompt.format(**{"query": query.role_content, "doc_infos": doc_infos, "formatted_tools": formatted_tools, "tool_names": tool_names}) - # return prompt def create_doc_prompt(self, message: Message) -> str: '''''' @@ -197,7 +262,7 @@ class BaseAgent: return "\n补充自身对话信息: " + selfmemory_message if selfmemory_message else None def init_history(self, memory: Memory = None) -> Memory: - return Memory([]) + return Memory(messages=[]) def update_history(self, message: Message): self.memory.append(message) @@ -212,216 +277,389 @@ class BaseAgent: def create_llm_engine(self, temperature=0.2, stop=None): return getChatModel(temperature=temperature, stop=stop) - def filter(self, message: Message, stop=None) -> Message: + # def filter(self, message: Message, stop=None) -> Message: - tool_params = self.parser_spec_key(message.role_content, "tool_params") - code_content = self.parser_spec_key(message.role_content, "code_content") - plan = self.parser_spec_key(message.role_content, "plan") - plans = self.parser_spec_key(message.role_content, "plans", do_search=False) - content = self.parser_spec_key(message.role_content, "content", do_search=False) + # tool_params = self.parser_spec_key(message.role_content, "tool_params") + # code_content = self.parser_spec_key(message.role_content, "code_content") + # plan = self.parser_spec_key(message.role_content, "plan") + # plans = self.parser_spec_key(message.role_content, "plans", do_search=False) + # content = self.parser_spec_key(message.role_content, "content", do_search=False) - # logger.debug(f"tool_params: {tool_params}, code_content: {code_content}, plan: {plan}, plans: {plans}, content: {content}") - role_content = tool_params or code_content or plan or plans or content - message.role_content = role_content or message.role_content - return message + # # logger.debug(f"tool_params: {tool_params}, code_content: {code_content}, plan: {plan}, plans: {plans}, content: {content}") + # role_content = tool_params or code_content or plan or plans or content + # message.role_content = role_content or message.role_content + # return message def token_usage(self, ): + '''calculate the usage of token''' pass - def get_extra_infos(self, message: Message) -> Message: - '''''' - if self.do_search: - message = self.get_search_retrieval(message) + 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.do_doc_retrieval: - message = self.get_doc_retrieval(message) - - if self.do_tool_retrieval: - message = self.get_tool_retrieval(message) + if self.focus_message_keys == []: + return 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 knowledge_basename: - docs = DocRetrieval.run(query, knowledge_basename, top_k, score_threshold) - message.db_docs = [Doc(**doc) for doc in docs] - return message - - def get_code_retrieval(self, message: Message) -> Message: - # DocRetrieval.run("langchain是什么", "DSADSAD") - query = message.input_query - code_engine_name = message.code_engine_name - history_node_list = message.history_node_list - code_docs = CodeRetrieval.run(code_engine_name, query, code_limit=message.top_k, history_node_list=history_node_list) - message.code_docs = [CodeDoc(**doc) for doc in code_docs] - return message - - def get_tool_retrieval(self, message: Message) -> Message: - return message - - def step_router(self, message: Message) -> Message: - '''''' - # message = self.parser(message) - # logger.debug(f"message.action_status: {message.action_status}") - if message.action_status == ActionStatus.CODING: - message = self.code_step(message) - elif message.action_status == ActionStatus.TOOL_USING: - message = self.tool_step(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 - return 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"执行上述代码后存在报错信息为 {code_answer.code_exe_response},需要进行修复" \ - if code_answer.code_exe_type == "error" else f"执行上述代码后返回信息为 {code_answer.code_exe_response}" - 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观察: 执行上述代码后生成一张图片, 图片名为{uid}\n" - message.observation = f"\n观察: 执行上述代码后生成一张图片, 图片名为{uid}\n" - message.step_content += f"\n观察: 执行上述代码后生成一张图片, 图片名为{uid}\n" - message.step_contents += [f"\n观察: 执行上述代码后生成一张图片, 图片名为{uid}\n"] - message.role_content += f"\n观察:执行上述代码后生成一张图片, 图片名为{uid}\n" - else: - message.code_answer = code_answer.code_exe_response - message.observation = code_answer.code_exe_response - message.step_content += f"\n观察: {code_prompt}\n" - message.step_contents += [f"\n观察: {code_prompt}\n"] - message.role_content += f"\n观察: {code_prompt}\n" - # logger.info(f"观察: {message.action_status}, {message.observation}") - return message - - def tool_step(self, message: Message) -> Message: - '''execute tool''' - # logger.debug(f"message: {message.action_status}, {message.tool_name}, {message.tool_params}") - tool_names = [tool.name for tool in message.tools] - if message.tool_name not in tool_names: - message.tool_answer = "不存在可以执行的tool" - message.observation = "不存在可以执行的tool" - message.role_content += f"\n观察: 不存在可以执行的tool\n" - message.step_content += f"\n观察: 不存在可以执行的tool\n" - message.step_contents += [f"\n观察: 不存在可以执行的tool\n"] - for tool in message.tools: - if tool.name == message.tool_name: - tool_res = tool.func(**message.tool_params) - message.tool_answer = tool_res - message.observation = tool_res - message.role_content += f"\n观察: {tool_res}\n" - message.step_content += f"\n观察: {tool_res}\n" - message.step_contents += [f"\n观察: {tool_res}\n"] - - # logger.info(f"观察: {message.action_status}, {message.observation}") - return message - - def parser(self, message: Message) -> Message: - '''''' - content = message.role_content - parser_keys = ["action", "code_content", "code_filename", "tool_params", "plans"] - try: - s_json = self._parse_json(content) - message.action_status = s_json.get("action") - message.code_content = s_json.get("code_content") - message.tool_params = s_json.get("tool_params") - message.tool_name = s_json.get("tool_name") - message.code_filename = s_json.get("code_filename") - message.plans = s_json.get("plans") - # for parser_key in parser_keys: - # message.action_status = content.get(parser_key) - except Exception as e: - # logger.warning(f"{traceback.format_exc()}") - action_value = self._match(r"'action':\s*'([^']*)'", content) if "'action'" in content else self._match(r'"action":\s*"([^"]*)"', content) - code_content_value = self._match(r"'code_content':\s*'([^']*)'", content) if "'code_content'" in content else self._match(r'"code_content":\s*"([^"]*)"', content) - filename_value = self._match(r"'code_filename':\s*'([^']*)'", content) if "'code_filename'" in content else self._match(r'"code_filename":\s*"([^"]*)"', content) - tool_params_value = self._match(r"'tool_params':\s*(\{[^{}]*\})", content, do_json=True) if "'tool_params'" in content \ - else self._match(r'"tool_params":\s*(\{[^{}]*\})', content, do_json=True) - tool_name_value = self._match(r"'tool_name':\s*'([^']*)'", content) if "'tool_name'" in content else self._match(r'"tool_name":\s*"([^"]*)"', content) - plans_value = self._match(r"'plans':\s*(\[.*?\])", content, do_search=False) if "'plans'" in content else self._match(r'"plans":\s*(\[.*?\])', content, do_search=False, ) - # re解析 - message.action_status = action_value or "default" - message.code_content = code_content_value - message.code_filename = filename_value - message.tool_params = tool_params_value - message.tool_name = tool_name_value - message.plans = plans_value - - # logger.debug(f"确认当前的action: {message.action_status}") - - return message - - def parser_spec_key(self, content, key, do_search=True, do_json=False) -> str: - '''''' - key2pattern = { - "'action'": r"'action':\s*'([^']*)'", '"action"': r'"action":\s*"([^"]*)"', - "'code_content'": r"'code_content':\s*'([^']*)'", '"code_content"': r'"code_content":\s*"([^"]*)"', - "'code_filename'": r"'code_filename':\s*'([^']*)'", '"code_filename"': r'"code_filename":\s*"([^"]*)"', - "'tool_params'": r"'tool_params':\s*(\{[^{}]*\})", '"tool_params"': r'"tool_params":\s*(\{[^{}]*\})', - "'tool_name'": r"'tool_name':\s*'([^']*)'", '"tool_name"': r'"tool_name":\s*"([^"]*)"', - "'plans'": r"'plans':\s*(\[.*?\])", '"plans"': r'"plans":\s*(\[.*?\])', - "'content'": r"'content':\s*'([^']*)'", '"content"': r'"content":\s*"([^"]*)"', - } + # def get_extra_infos(self, message: Message) -> Message: + # '''''' + # if self.do_search: + # message = self.get_search_retrieval(message) - s_json = self._parse_json(content) - try: - if s_json and key in s_json: - return str(s_json[key]) - except: - pass + # if self.do_doc_retrieval: + # message = self.get_doc_retrieval(message) - keystr = f"'{key}'" if f"'{key}'" in content else f'"{key}"' - return self._match(key2pattern.get(keystr, fr"'{key}':\s*'([^']*)'"), content, do_search=do_search, do_json=do_json) + # if self.do_tool_retrieval: + # message = self.get_tool_retrieval(message) + + # return message - def _match(self, pattern, s, do_search=True, do_json=False): - try: - if do_search: - match = re.search(pattern, s) - if match: - value = match.group(1).replace("\\n", "\n") - if do_json: - value = json.loads(value) - else: - value = None - else: - match = re.findall(pattern, s, re.DOTALL) - if match: - value = match[0] - if do_json: - value = json.loads(value) - else: - value = None - except Exception as e: - logger.warning(f"{traceback.format_exc()}") + # 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 knowledge_basename: + # docs = DocRetrieval.run(query, knowledge_basename, top_k, score_threshold) + # message.db_docs = [Doc(**doc) for doc in docs] + # return message + + # def get_code_retrieval(self, message: Message) -> Message: + # # DocRetrieval.run("langchain是什么", "DSADSAD") + # query = message.input_query + # code_engine_name = message.code_engine_name + # history_node_list = message.history_node_list + # code_docs = CodeRetrieval.run(code_engine_name, query, code_limit=message.top_k, history_node_list=history_node_list) + # message.code_docs = [CodeDoc(**doc) for doc in code_docs] + # return message + + # def get_tool_retrieval(self, message: Message) -> Message: + # return message + + # def step_router(self, message: Message) -> tuple[Message, ...]: + # '''''' + # # message = self.parser(message) + # # logger.debug(f"message.action_status: {message.action_status}") + # observation_message = None + # if message.action_status == ActionStatus.CODING: + # message, observation_message = self.code_step(message) + # elif message.action_status == ActionStatus.TOOL_USING: + # message, observation_message = self.tool_step(message) - # logger.debug(f"pattern: {pattern}, s: {s}, match: {match}") - return value + # 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"执行上述代码后存在报错信息为 {code_answer.code_exe_response},需要进行修复" \ + # if code_answer.code_exe_type == "error" else f"执行上述代码后返回信息为 {code_answer.code_exe_response}" + + # observation_message = Message( + # role_name="observation", + # role_type="func", #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:**: 执行上述代码后生成一张图片, 图片名为{uid}\n" + # message.observation = f"\n**Observation:**: 执行上述代码后生成一张图片, 图片名为{uid}\n" + # message.step_content += f"\n**Observation:**: 执行上述代码后生成一张图片, 图片名为{uid}\n" + # message.step_contents += [f"\n**Observation:**: 执行上述代码后生成一张图片, 图片名为{uid}\n"] + # # message.role_content += f"\n**Observation:**:执行上述代码后生成一张图片, 图片名为{uid}\n" + # observation_message.role_content = f"\n**Observation:**: 执行上述代码后生成一张图片, 图片名为{uid}\n" + # observation_message.parsed_output = {"Observation": f"执行上述代码后生成一张图片, 图片名为{uid}"} + # 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.step_contents += [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": code_prompt} + # # logger.info(f"**Observation:** {message.action_status}, {message.observation}") + # return message, observation_message + + # def tool_step(self, message: Message) -> Message: + # '''execute tool''' + # # logger.debug(f"{message}") + # observation_message = Message( + # 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, + # ) + # # logger.debug(f"message: {message.action_status}, {message.tool_name}, {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" + # message.step_contents += [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"} + # for tool in message.tools: + # if tool.name == message.tool_name: + # tool_res = tool.func(**message.tool_params.get("tool_params", {})) + # logger.debug(f"tool_res {tool_res}") + # 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" + # message.step_contents += [f"\n**Observation:** {tool_res}\n"] + # observation_message.role_content = f"\n**Observation:** {tool_res}\n" + # observation_message.parsed_output = {"Observation": tool_res} + # break + + # # logger.info(f"**Observation:** {message.action_status}, {message.observation}") + # return message, observation_message - def _parse_json(self, s): - try: - pattern = r"```([^`]+)```" - match = re.findall(pattern, s) - if match: - return eval(match[0]) - except: - pass - return None + # def parser(self, message: Message) -> Message: + # '''''' + # content = message.role_content + # parser_keys = ["action", "code_content", "code_filename", "tool_params", "plans"] + # try: + # s_json = self._parse_json(content) + # message.action_status = s_json.get("action") + # message.code_content = s_json.get("code_content") + # message.tool_params = s_json.get("tool_params") + # message.tool_name = s_json.get("tool_name") + # message.code_filename = s_json.get("code_filename") + # message.plans = s_json.get("plans") + # # for parser_key in parser_keys: + # # message.action_status = content.get(parser_key) + # except Exception as e: + # # logger.warning(f"{traceback.format_exc()}") + # 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(.*?)```' + + # # 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]: + # if "PLAN" != k: continue + # 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 extract_content_from_backticks(text): + # code_blocks = [] + # lines = text.split('\n') + # is_code_block = False + # code_block = '' + # language = '' + # for line in lines: + # if line.startswith('```') and not is_code_block: + # is_code_block = True + # language = line[3:] + # code_block = '' + # elif line.startswith('```') and is_code_block: + # is_code_block = False + # code_blocks.append({language.strip(): code_block.strip()}) + # elif is_code_block: + # code_block += line + '\n' + # return code_blocks + + # def parse_dict_to_dict(parsed_dict): + # code_pattern = r'```python\n(.*?)```' + # tool_pattern = r'```tool_params\n(.*?)```' + + # pattern_dict = {"code": code_pattern, "tool_params": tool_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): 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()) + # except: + # spec_parsed_dict[key] = _match.group(1).strip() + # break + # return spec_parsed_dict + # def parse_dict_to_dict(parsed_dict): + # code_pattern = r'```python\n(.*?)```' + # tool_pattern = r'```json\n(.*?)```' + + # pattern_dict = {"code": code_pattern, "json": tool_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): continue + # _match = re.search(pattern, text, re.DOTALL) + # if _match: + # # Add the code block to the dictionary + # logger.debug(f"dsadsa {text}") + # try: + # spec_parsed_dict[key] = json.loads(_match.group(1).strip()) + # except: + # spec_parsed_dict[key] = _match.group(1).strip() + # break + # return spec_parsed_dict + + # parsed_dict = parse_text_to_dict(content) + # spec_parsed_dict = parse_dict_to_dict(parsed_dict) + # action_value = parsed_dict.get('Action Status') + # if action_value: + # action_value = action_value.lower() + # logger.info(f'{self.role.role_name}: action_value: {action_value}') + # # action_value = self._match(r"'action':\s*'([^']*)'", content) if "'action'" in content else self._match(r'"action":\s*"([^"]*)"', content) + + # code_content_value = spec_parsed_dict.get('code') + # # code_content_value = self._match(r"'code_content':\s*'([^']*)'", content) if "'code_content'" in content else self._match(r'"code_content":\s*"([^"]*)"', content) + # filename_value = self._match(r"'code_filename':\s*'([^']*)'", content) if "'code_filename'" in content else self._match(r'"code_filename":\s*"([^"]*)"', content) + # tool_params_value = spec_parsed_dict.get('tool_params') + # # tool_params_value = self._match(r"'tool_params':\s*(\{[^{}]*\})", content, do_json=True) if "'tool_params'" in content \ + # # else self._match(r'"tool_params":\s*(\{[^{}]*\})', content, do_json=True) + # tool_name_value = self._match(r"'tool_name':\s*'([^']*)'", content) if "'tool_name'" in content else self._match(r'"tool_name":\s*"([^"]*)"', content) + # plans_value = self._match(r"'plans':\s*(\[.*?\])", content, do_search=False) if "'plans'" in content else self._match(r'"plans":\s*(\[.*?\])', content, do_search=False, ) + # # re解析 + # message.action_status = action_value or "default" + # message.code_content = code_content_value + # message.code_filename = filename_value + # message.tool_params = tool_params_value + # message.tool_name = tool_name_value + # message.plans = plans_value + # message.parsed_output = parsed_dict + # message.spec_parsed_output = spec_parsed_dict + # code_content_value = spec_parsed_dict.get('code') + # # code_content_value = self._match(r"'code_content':\s*'([^']*)'", content) if "'code_content'" in content else self._match(r'"code_content":\s*"([^"]*)"', content) + # filename_value = self._match(r"'code_filename':\s*'([^']*)'", content) if "'code_filename'" in content else self._match(r'"code_filename":\s*"([^"]*)"', content) + # logger.debug(spec_parsed_dict) + + # if action_value == 'tool_using': + # tool_params_value = spec_parsed_dict.get('json') + # else: + # tool_params_value = None + # # tool_params_value = self._match(r"'tool_params':\s*(\{[^{}]*\})", content, do_json=True) if "'tool_params'" in content \ + # # else self._match(r'"tool_params":\s*(\{[^{}]*\})', content, do_json=True) + # tool_name_value = self._match(r"'tool_name':\s*'([^']*)'", content) if "'tool_name'" in content else self._match(r'"tool_name":\s*"([^"]*)"', content) + # plans_value = self._match(r"'plans':\s*(\[.*?\])", content, do_search=False) if "'plans'" in content else self._match(r'"plans":\s*(\[.*?\])', content, do_search=False, ) + # # re解析 + # message.action_status = action_value or "default" + # message.code_content = code_content_value + # message.code_filename = filename_value + # message.tool_params = tool_params_value + # message.tool_name = tool_name_value + # message.plans = plans_value + # message.parsed_output = parsed_dict + # message.spec_parsed_output = spec_parsed_dict + + # # logger.debug(f"确认当前的action: {message.action_status}") + + # return message + + # def parser_spec_key(self, content, key, do_search=True, do_json=False) -> str: + # '''''' + # key2pattern = { + # "'action'": r"'action':\s*'([^']*)'", '"action"': r'"action":\s*"([^"]*)"', + # "'code_content'": r"'code_content':\s*'([^']*)'", '"code_content"': r'"code_content":\s*"([^"]*)"', + # "'code_filename'": r"'code_filename':\s*'([^']*)'", '"code_filename"': r'"code_filename":\s*"([^"]*)"', + # "'tool_params'": r"'tool_params':\s*(\{[^{}]*\})", '"tool_params"': r'"tool_params":\s*(\{[^{}]*\})', + # "'tool_name'": r"'tool_name':\s*'([^']*)'", '"tool_name"': r'"tool_name":\s*"([^"]*)"', + # "'plans'": r"'plans':\s*(\[.*?\])", '"plans"': r'"plans":\s*(\[.*?\])', + # "'content'": r"'content':\s*'([^']*)'", '"content"': r'"content":\s*"([^"]*)"', + # } + + # s_json = self._parse_json(content) + # try: + # if s_json and key in s_json: + # return str(s_json[key]) + # except: + # pass + + # keystr = f"'{key}'" if f"'{key}'" in content else f'"{key}"' + # return self._match(key2pattern.get(keystr, fr"'{key}':\s*'([^']*)'"), content, do_search=do_search, do_json=do_json) + + # def _match(self, pattern, s, do_search=True, do_json=False): + # try: + # if do_search: + # match = re.search(pattern, s) + # if match: + # value = match.group(1).replace("\\n", "\n") + # if do_json: + # value = json.loads(value) + # else: + # value = None + # else: + # match = re.findall(pattern, s, re.DOTALL) + # if match: + # value = match[0] + # if do_json: + # value = json.loads(value) + # else: + # value = None + # except Exception as e: + # logger.warning(f"{traceback.format_exc()}") + + # # logger.debug(f"pattern: {pattern}, s: {s}, match: {match}") + # return value + + # def _parse_json(self, s): + # try: + # pattern = r"```([^`]+)```" + # match = re.findall(pattern, s) + # if match: + # return eval(match[0]) + # except: + # pass + # return None - def get_memory(self, ): + def get_memory(self, content_key="role_content"): return self.memory.to_tuple_messages(content_key="step_content") - def get_memory_str(self, ): + 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")]) \ No newline at end of file diff --git a/dev_opsgpt/connector/agents/check_agent.py b/dev_opsgpt/connector/agents/check_agent.py new file mode 100644 index 0000000..7e2ea63 --- /dev/null +++ b/dev_opsgpt/connector/agents/check_agent.py @@ -0,0 +1,110 @@ +from pydantic import BaseModel +from typing import List, Union +import re +import json +import traceback +import copy +from loguru import logger + +from langchain.prompts.chat import ChatPromptTemplate + +from dev_opsgpt.connector.schema import ( + Memory, Task, Env, Role, Message, ActionStatus +) +from dev_opsgpt.llm_models import getChatModel +from dev_opsgpt.connector.configs.agent_config import REACT_PROMPT_INPUT, CONTEXT_PROMPT_INPUT, QUERY_CONTEXT_PROMPT_INPUT + +from .base_agent import BaseAgent + + +class CheckAgent(BaseAgent): + def __init__( + self, + role: Role, + task: Task = None, + memory: Memory = None, + chat_turn: int = 1, + do_search: bool = False, + do_doc_retrieval: bool = False, + do_tool_retrieval: bool = False, + temperature: float = 0.2, + stop: Union[List[str], str] = None, + do_filter: bool = True, + do_use_self_memory: bool = True, + focus_agents: List[str] = [], + focus_message_keys: List[str] = [], + # prompt_mamnger: PromptManager + ): + + super().__init__(role, task, memory, chat_turn, do_search, do_doc_retrieval, + do_tool_retrieval, temperature, stop, do_filter,do_use_self_memory, + focus_agents, focus_message_keys + ) + + def create_prompt( + self, query: Message, memory: Memory =None, history: Memory = None, background: Memory = None, memory_pool: Memory=None, prompt_mamnger=None) -> str: + ''' + role\task\tools\docs\memory + ''' + # + doc_infos = self.create_doc_prompt(query) + code_infos = self.create_codedoc_prompt(query) + # + formatted_tools, tool_names = self.create_tools_prompt(query) + task_prompt = self.create_task_prompt(query) + background_prompt = self.create_background_prompt(background) + history_prompt = self.create_history_prompt(history) + selfmemory_prompt = self.create_selfmemory_prompt(memory, control_key="step_content") + + # react 流程是自身迭代过程,另外二次触发的是需要作为历史对话信息 + # input_query = react_memory.to_tuple_messages(content_key="step_content") + input_query = query.input_query + + # logger.debug(f"{self.role.role_name} extra_system_prompt: {self.role.role_prompt}") + # logger.debug(f"{self.role.role_name} input_query: {input_query}") + # logger.debug(f"{self.role.role_name} doc_infos: {doc_infos}") + # logger.debug(f"{self.role.role_name} tool_names: {tool_names}") + # prompt += "\n" + CHECK_PROMPT_INPUT.format(**{"query": input_query}) + # prompt.format(**{"query": input_query}) + + # extra_system_prompt = self.role.role_prompt + prompt = self.role.role_prompt.format(**{"query": input_query, "formatted_tools": formatted_tools, "tool_names": tool_names}) + + if "**Context:**" in self.role.role_prompt: + # logger.debug(f"parsed_output_list: {query.parsed_output_list}") + # input_query = "'''" + "\n".join([f"*{k}*\n{v}" for i in background.get_parserd_output_list() for k,v in i.items() if "Action Status" !=k]) + "'''" + context = "\n".join([f"*{k}*\n{v}" for i in background.get_parserd_output_list() for k,v in i.items() if "Action Status" !=k]) + # logger.debug(context) + # logger.debug(f"parsed_output_list: {t}") + prompt += "\n" + QUERY_CONTEXT_PROMPT_INPUT.format(**{"query": query.origin_query, "context": context}) + else: + prompt += "\n" + REACT_PROMPT_INPUT.format(**{"query": input_query}) + + + task = query.task or self.task + if task_prompt is not None: + prompt += "\n" + task.task_prompt + + # if doc_infos is not None and doc_infos!="" and doc_infos!="不存在知识库辅助信息": + # prompt += f"\n知识库信息: {doc_infos}" + + # if code_infos is not None and code_infos!="" and code_infos!="不存在代码库辅助信息": + # prompt += f"\n代码库信息: {code_infos}" + + # if background_prompt: + # prompt += "\n" + background_prompt + + # if history_prompt: + # prompt += "\n" + history_prompt + + # if selfmemory_prompt: + # prompt += "\n" + selfmemory_prompt + + # prompt = extra_system_prompt.format(**{"query": input_query, "doc_infos": doc_infos, "formatted_tools": formatted_tools, "tool_names": tool_names}) + while "{{" in prompt or "}}" in prompt: + prompt = prompt.replace("{{", "{") + prompt = prompt.replace("}}", "}") + + # logger.debug(f"{self.role.role_name} prompt: {prompt}") + return prompt + diff --git a/dev_opsgpt/connector/agents/executor_agent.py b/dev_opsgpt/connector/agents/executor_agent.py new file mode 100644 index 0000000..d92222e --- /dev/null +++ b/dev_opsgpt/connector/agents/executor_agent.py @@ -0,0 +1,214 @@ +from pydantic import BaseModel +from typing import List, Union, Tuple, Any +import re +import json +import traceback +import copy +from loguru import logger + +from langchain.prompts.chat import ChatPromptTemplate + +from dev_opsgpt.connector.schema import ( + Memory, Task, Env, Role, Message, ActionStatus +) +from dev_opsgpt.llm_models import getChatModel +from dev_opsgpt.connector.configs.prompts import EXECUTOR_PROMPT_INPUT, BEGIN_PROMPT_INPUT +from dev_opsgpt.connector.utils import parse_section + +from .base_agent import BaseAgent + + +class ExecutorAgent(BaseAgent): + def __init__( + self, + role: Role, + task: Task = None, + memory: Memory = None, + chat_turn: int = 1, + do_search: bool = False, + do_doc_retrieval: bool = False, + do_tool_retrieval: bool = False, + temperature: float = 0.2, + stop: Union[List[str], str] = None, + do_filter: bool = True, + do_use_self_memory: bool = True, + focus_agents: List[str] = [], + focus_message_keys: List[str] = [], + # prompt_mamnger: PromptManager + ): + + super().__init__(role, task, memory, chat_turn, do_search, do_doc_retrieval, + do_tool_retrieval, temperature, stop, do_filter,do_use_self_memory, + focus_agents, focus_message_keys + ) + self.do_all_task = True # run all tasks + + def arun(self, query: Message, history: Memory = None, background: Memory = None, memory_pool: Memory=None) -> Message: + '''agent reponse from multi-message''' + # insert query into memory + task_executor_memory = Memory(messages=[]) + # insert query + output_message = Message( + role_name=self.role.role_name, + role_type="ai", #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] + ) + + 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.parsed_output = {"Question": 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_pool, 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: + logger.debug(f"{query.parsed_output['PLAN']}") + 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 = {"Question": 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_pool, task_executor_memory): + pass + yield output_message + else: + query_c = copy.deepcopy(query) + task_content = query_c.parsed_output["PLAN"][plan_step] + query_c.parsed_output = {"Question": 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_pool, 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) + # logger.info(f"{self.role.role_name} currenct question: {output_message.input_query}\nllm_executor_run: {output_message.step_content}") + # logger.info(f"{self.role.role_name} currenct parserd_output_list: {output_message.parserd_output_list}") + output_message.input_query = output_message.role_content + # update memory pool + memory_pool.append(output_message) + yield output_message + + def _arun_step(self, output_message: Message, query: Message, self_memory: Memory, + history: Memory, background: Memory, memory_pool: Memory, + react_memory: Memory) -> Union[Message, Memory]: + '''execute the llm predict by created prompt''' + prompt = self.create_prompt(query, self_memory, history, background, memory_pool=memory_pool, react_memory=react_memory) + content = self.llm.predict(prompt) + # logger.debug(f"{self.role.role_name} prompt: {prompt}") + logger.debug(f"{self.role.role_name} content: {content}") + + output_message.role_content = content + output_message.role_contents += [content] + output_message.step_content += "\n"+output_message.role_content + output_message.step_contents + [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) + # logger.debug(f"{self.role.role_name} content: {content}") + # update parserd_output_list + 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}") + yield output_message, react_memory + + def create_prompt( + self, query: Message, memory: Memory =None, history: Memory = None, background: Memory = None, memory_pool: Memory=None, react_memory: Memory = None, prompt_mamnger=None) -> str: + ''' + role\task\tools\docs\memory + ''' + # + doc_infos = self.create_doc_prompt(query) + code_infos = self.create_codedoc_prompt(query) + # + formatted_tools, tool_names = self.create_tools_prompt(query) + task_prompt = self.create_task_prompt(query) + background_prompt = self.create_background_prompt(background, control_key="step_content") + history_prompt = self.create_history_prompt(history) + selfmemory_prompt = self.create_selfmemory_prompt(memory, control_key="step_content") + + # + memory_pool_select_by_agent_key = self.select_memory_by_agent_key(memory_pool) + memory_pool_select_by_agent_key_context = '\n\n'.join([ + f"*{k}*\n{v}" for parsed_output in memory_pool_select_by_agent_key.get_parserd_output_list() for k, v in parsed_output.items() if k not in ['Action Status'] + ]) + + DocInfos = "" + if doc_infos is not None and doc_infos!="" and doc_infos!="不存在知识库辅助信息": + DocInfos += f"\nDocument Information: {doc_infos}" + + if code_infos is not None and code_infos!="" and code_infos!="不存在代码库辅助信息": + DocInfos += f"\nCodeBase Infomation: {code_infos}" + + # extra_system_prompt = self.role.role_prompt + prompt = self.role.role_prompt.format(**{"formatted_tools": formatted_tools, "tool_names": tool_names}) + + # input_query = react_memory.to_tuple_messages(content_key="role_content") + # logger.debug(f"get_parserd_dict {react_memory.get_parserd_output()}") + input_query = "\n".join(["\n".join([f"**{k}:**\n{v}" for k,v in _dict.items()]) for _dict in react_memory.get_parserd_output()]) + # input_query = query.input_query + "\n".join([f"{v}" for k, v in input_query if v]) + last_agent_parsed_output = "\n".join(["\n".join([f"*{k}*\n{v}" for k,v in _dict.items()]) for _dict in query.parsed_output_list]) + react_parsed_output = "\n".join(["\n".join([f"*{k}_context*\n{v}" for k,v in _dict.items()]) for _dict in react_memory.get_parserd_output()[:-1]]) + # + prompt += "\n" + BEGIN_PROMPT_INPUT + + input_keys = parse_section(self.role.role_prompt, 'Input Format') + if input_keys: + for input_key in input_keys: + if input_key == "Origin Query": + prompt += "\n**Origin Query:**\n" + query.origin_query + elif input_key == "DocInfos": + prompt += "\n**DocInfos:**\n" + DocInfos + elif input_key == "Context": + if self.focus_agents and memory_pool_select_by_agent_key_context: + context = memory_pool_select_by_agent_key_context + else: + context = last_agent_parsed_output + prompt += "\n**Context:**\n" + context + f"\n{react_parsed_output}" + elif input_key == "Question": + prompt += "\n**Question:**\n" + query.parsed_output.get("Question") + else: + prompt += "\n" + input_query + + task = query.task or self.task + # if task_prompt is not None: + # prompt += "\n" + task.task_prompt + + # if selfmemory_prompt: + # prompt += "\n" + selfmemory_prompt + + # if background_prompt: + # prompt += "\n" + background_prompt + + # if history_prompt: + # prompt += "\n" + history_prompt + + # prompt = extra_system_prompt.format(**{"query": input_query, "doc_infos": doc_infos, "formatted_tools": formatted_tools, "tool_names": tool_names}) + while "{{" in prompt or "}}" in prompt: + prompt = prompt.replace("{{", "{") + prompt = prompt.replace("}}", "}") + return prompt + + def set_task(self, do_all_task): + '''set task exec type''' + self.do_all_task = do_all_task \ No newline at end of file diff --git a/dev_opsgpt/connector/agents/react_agent.py b/dev_opsgpt/connector/agents/react_agent.py index 4625f74..e20b049 100644 --- a/dev_opsgpt/connector/agents/react_agent.py +++ b/dev_opsgpt/connector/agents/react_agent.py @@ -1,15 +1,16 @@ from pydantic import BaseModel from typing import List, Union import re +import json import traceback import copy from loguru import logger from langchain.prompts.chat import ChatPromptTemplate -from dev_opsgpt.connector.connector_schema import Message -from dev_opsgpt.connector.shcema.memory import Memory -from dev_opsgpt.connector.connector_schema import Task, Env, Role, Message, ActionStatus +from dev_opsgpt.connector.schema import ( + Memory, Task, Env, Role, Message, ActionStatus +) from dev_opsgpt.llm_models import getChatModel from dev_opsgpt.connector.configs.agent_config import REACT_PROMPT_INPUT @@ -27,63 +28,93 @@ class ReactAgent(BaseAgent): do_doc_retrieval: bool = False, do_tool_retrieval: bool = False, temperature: float = 0.2, - stop: Union[List[str], str] = "观察", + stop: Union[List[str], str] = None, do_filter: bool = True, do_use_self_memory: bool = True, - # docs_prompt: str, + focus_agents: List[str] = [], + focus_message_keys: List[str] = [], # prompt_mamnger: PromptManager ): + super().__init__(role, task, memory, chat_turn, do_search, do_doc_retrieval, - do_tool_retrieval, temperature, stop, do_filter,do_use_self_memory + do_tool_retrieval, temperature, stop, do_filter,do_use_self_memory, + focus_agents, focus_message_keys ) - def run(self, query: Message, history: Memory = None, background: Memory = None) -> Message: + def run(self, query: Message, history: Memory = None, background: Memory = None, memory_pool: Memory = None) -> Message: + '''agent reponse from multi-message''' + for message in self.arun(query, history, background, memory_pool): + pass + return message + + def arun(self, query: Message, history: Memory = None, background: Memory = None, memory_pool: Memory = None) -> Message: + '''agent reponse from multi-message''' step_nums = copy.deepcopy(self.chat_turn) - react_memory = Memory([]) - # 问题插入 + react_memory = Memory(messages=[]) + # insert query output_message = Message( role_name=self.role.role_name, role_type="ai", #self.role.role_type, role_content=query.input_query, - step_content=query.input_query, + step_content="", input_query=query.input_query, - tools=query.tools + tools=query.tools, + parsed_output_list=[query.parsed_output] ) - react_memory.append(output_message) + query_c = copy.deepcopy(query) + query_c.parsed_output = {"Question": "\n".join([f"{v}" for k, v in query.parsed_output.items() if k not in ["Action Status"]])} + 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 - self_memory = self.memory if self.do_use_self_memory else None - prompt = self.create_prompt(query, self_memory, history, background, react_memory) + prompt = self.create_prompt(query, self_memory, history, background, react_memory, memory_pool) try: content = self.llm.predict(prompt) except Exception as e: logger.warning(f"error prompt: {prompt}") raise Exception(traceback.format_exc()) - output_message.role_content = content + output_message.role_content = "\n"+content output_message.role_contents += [content] - output_message.step_content += output_message.role_content + output_message.step_content += "\n"+output_message.role_content output_message.step_contents + [output_message.role_content] + yield output_message # logger.debug(f"{self.role.role_name}, {idx} iteration prompt: {prompt}") - # logger.info(f"{self.role.role_name}, {idx} iteration step_run: {output_message.role_content}") + logger.info(f"{self.role.role_name}, {idx} iteration step_run: {output_message.role_content}") - output_message = self.parser(output_message) + output_message = self.message_utils.parser(output_message) # when get finished signal can stop early if output_message.action_status == ActionStatus.FINISHED: break # according the output to choose one action for code_content or tool_content - output_message = self.step_router(output_message) - logger.info(f"{self.role.role_name} react_run: {output_message.role_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}") + # logger.info(f"{self.role.role_name} currenct question: {output_message.input_query}\nllm_react_run: {output_message.role_content}") + idx += 1 step_nums -= 1 + yield output_message # react' self_memory saved at last self.append_history(output_message) - return output_message - + # update memory pool + # memory_pool.append(output_message) + output_message.input_query = query.input_query + # update memory pool + memory_pool.append(output_message) + yield output_message + def create_prompt( - self, query: Message, memory: Memory =None, history: Memory = None, background: Memory = None, react_memory: Memory = None, prompt_mamnger=None) -> str: + self, query: Message, memory: Memory =None, history: Memory = None, background: Memory = None, react_memory: Memory = None, memory_pool: Memory= None, + prompt_mamnger=None) -> str: ''' role\task\tools\docs\memory ''' @@ -100,35 +131,39 @@ class ReactAgent(BaseAgent): # extra_system_prompt = self.role.role_prompt prompt = self.role.role_prompt.format(**{"formatted_tools": formatted_tools, "tool_names": tool_names}) + # react 流程是自身迭代过程,另外二次触发的是需要作为历史对话信息 + # input_query = react_memory.to_tuple_messages(content_key="step_content") + # # input_query = query.input_query + "\n" + "\n".join([f"{v}" for k, v in input_query if v]) + # input_query = "\n".join([f"{v}" for k, v in input_query if v]) + input_query = "\n".join(["\n".join([f"**{k}:**\n{v}" for k,v in _dict.items()]) for _dict in react_memory.get_parserd_output()]) + logger.debug(f"input_query: {input_query}") + + prompt += "\n" + REACT_PROMPT_INPUT.format(**{"query": input_query}) task = query.task or self.task - if task_prompt is not None: - prompt += "\n" + task.task_prompt + # if task_prompt is not None: + # prompt += "\n" + task.task_prompt - if doc_infos is not None and doc_infos!="" and doc_infos!="不存在知识库辅助信息": - prompt += f"\n知识库信息: {doc_infos}" + # if doc_infos is not None and doc_infos!="" and doc_infos!="不存在知识库辅助信息": + # prompt += f"\n知识库信息: {doc_infos}" - if code_infos is not None and code_infos!="" and code_infos!="不存在代码库辅助信息": - prompt += f"\n代码库信息: {code_infos}" + # if code_infos is not None and code_infos!="" and code_infos!="不存在代码库辅助信息": + # prompt += f"\n代码库信息: {code_infos}" - if background_prompt: - prompt += "\n" + background_prompt + # if background_prompt: + # prompt += "\n" + background_prompt - if history_prompt: - prompt += "\n" + history_prompt + # if history_prompt: + # prompt += "\n" + history_prompt - if selfmemory_prompt: - prompt += "\n" + selfmemory_prompt - - # react 流程是自身迭代过程,另外二次触发的是需要作为历史对话信息 - input_query = react_memory.to_tuple_messages(content_key="step_content") - input_query = "\n".join([f"{v}" for k, v in input_query if v]) + # if selfmemory_prompt: + # prompt += "\n" + selfmemory_prompt # logger.debug(f"{self.role.role_name} extra_system_prompt: {self.role.role_prompt}") # logger.debug(f"{self.role.role_name} input_query: {input_query}") # logger.debug(f"{self.role.role_name} doc_infos: {doc_infos}") # logger.debug(f"{self.role.role_name} tool_names: {tool_names}") - prompt += "\n" + REACT_PROMPT_INPUT.format(**{"query": input_query}) + # prompt += "\n" + REACT_PROMPT_INPUT.format(**{"query": input_query}) # prompt = extra_system_prompt.format(**{"query": input_query, "doc_infos": doc_infos, "formatted_tools": formatted_tools, "tool_names": tool_names}) while "{{" in prompt or "}}" in prompt: diff --git a/dev_opsgpt/connector/agents/selector_agent.py b/dev_opsgpt/connector/agents/selector_agent.py new file mode 100644 index 0000000..951ec87 --- /dev/null +++ b/dev_opsgpt/connector/agents/selector_agent.py @@ -0,0 +1,109 @@ +from pydantic import BaseModel +from typing import List, Union +import re +import json +import traceback +import copy +from loguru import logger + +from langchain.prompts.chat import ChatPromptTemplate + +from dev_opsgpt.connector.schema import ( + Memory, Task, Env, Role, Message, ActionStatus +) +from dev_opsgpt.llm_models import getChatModel +from dev_opsgpt.connector.configs.agent_config import REACT_PROMPT_INPUT, CONTEXT_PROMPT_INPUT, QUERY_CONTEXT_PROMPT_INPUT + +from .base_agent import BaseAgent + + +class SelectorAgent(BaseAgent): + def __init__( + self, + role: Role, + task: Task = None, + memory: Memory = None, + chat_turn: int = 1, + do_search: bool = False, + do_doc_retrieval: bool = False, + do_tool_retrieval: bool = False, + temperature: float = 0.2, + stop: Union[List[str], str] = None, + do_filter: bool = True, + do_use_self_memory: bool = True, + focus_agents: List[str] = [], + focus_message_keys: List[str] = [], + # prompt_mamnger: PromptManager + ): + + super().__init__(role, task, memory, chat_turn, do_search, do_doc_retrieval, + do_tool_retrieval, temperature, stop, do_filter,do_use_self_memory, + focus_agents, focus_message_keys + ) + + def create_prompt( + self, query: Message, memory: Memory =None, history: Memory = None, background: Memory = None, memory_pool: Memory=None, prompt_mamnger=None) -> str: + ''' + role\task\tools\docs\memory + ''' + # + doc_infos = self.create_doc_prompt(query) + code_infos = self.create_codedoc_prompt(query) + # + formatted_tools, tool_names = self.create_tools_prompt(query) + task_prompt = self.create_task_prompt(query) + background_prompt = self.create_background_prompt(background) + history_prompt = self.create_history_prompt(history) + selfmemory_prompt = self.create_selfmemory_prompt(memory, control_key="step_content") + + # react 流程是自身迭代过程,另外二次触发的是需要作为历史对话信息 + # input_query = react_memory.to_tuple_messages(content_key="step_content") + input_query = query.input_query + + # logger.debug(f"{self.role.role_name} extra_system_prompt: {self.role.role_prompt}") + logger.debug(f"{self.role.role_name} input_query: {input_query}") + # logger.debug(f"{self.role.role_name} doc_infos: {doc_infos}") + # logger.debug(f"{self.role.role_name} tool_names: {tool_names}") + # prompt += "\n" + CHECK_PROMPT_INPUT.format(**{"query": input_query}) + # prompt.format(**{"query": input_query}) + + # extra_system_prompt = self.role.role_prompt + prompt = self.role.role_prompt.format(**{"query": input_query, "formatted_tools": formatted_tools, "tool_names": tool_names}) + + if "**Context:**" in self.role.role_prompt: + # logger.debug(f"parsed_output_list: {query.parsed_output_list}") + # input_query = "'''" + "\n".join([f"*{k}*\n{v}" for i in background.get_parserd_output_list() for k,v in i.items() if "Action Status" !=k]) + "'''" + context = "\n".join([f"*{k}*\n{v}" for i in background.get_parserd_output_list() for k,v in i.items() if "Action Status" !=k]) + # logger.debug(f"parsed_output_list: {t}") + prompt += "\n" + QUERY_CONTEXT_PROMPT_INPUT.format(**{"query": query.origin_query, "context": context}) + else: + prompt += "\n" + REACT_PROMPT_INPUT.format(**{"query": input_query}) + + + task = query.task or self.task + if task_prompt is not None: + prompt += "\n" + task.task_prompt + + # if doc_infos is not None and doc_infos!="" and doc_infos!="不存在知识库辅助信息": + # prompt += f"\n知识库信息: {doc_infos}" + + # if code_infos is not None and code_infos!="" and code_infos!="不存在代码库辅助信息": + # prompt += f"\n代码库信息: {code_infos}" + + # if background_prompt: + # prompt += "\n" + background_prompt + + # if history_prompt: + # prompt += "\n" + history_prompt + + # if selfmemory_prompt: + # prompt += "\n" + selfmemory_prompt + + # prompt = extra_system_prompt.format(**{"query": input_query, "doc_infos": doc_infos, "formatted_tools": formatted_tools, "tool_names": tool_names}) + while "{{" in prompt or "}}" in prompt: + prompt = prompt.replace("{{", "{") + prompt = prompt.replace("}}", "}") + + # logger.debug(f"{self.role.role_name} prompt: {prompt}") + return prompt + diff --git a/dev_opsgpt/connector/chains/base_chain.py b/dev_opsgpt/connector/chains/base_chain.py index dc49755..59d6e9e 100644 --- a/dev_opsgpt/connector/chains/base_chain.py +++ b/dev_opsgpt/connector/chains/base_chain.py @@ -7,13 +7,14 @@ import traceback import uuid import copy -from dev_opsgpt.connector.agents import BaseAgent +from dev_opsgpt.connector.agents import BaseAgent, CheckAgent from dev_opsgpt.tools.base_tool import BaseTools, Tool -from dev_opsgpt.connector.shcema.memory import Memory -from dev_opsgpt.connector.connector_schema import ( - Role, Message, ActionStatus, ChainConfig, + +from dev_opsgpt.connector.schema import ( + Memory, Role, Message, ActionStatus, ChainConfig, load_role_configs ) +from dev_opsgpt.connector.message_process import MessageUtils from dev_opsgpt.sandbox import PyCodeBox, CodeBoxResponse @@ -37,7 +38,7 @@ class BaseChain: self.agents = agents self.chat_turn = chat_turn self.do_checker = do_checker - self.checker = BaseAgent(role=role_configs["checker"].role, + self.checker = CheckAgent(role=role_configs["checker"].role, task = None, memory = None, do_search = role_configs["checker"].do_search, @@ -45,37 +46,64 @@ class BaseChain: do_tool_retrieval = role_configs["checker"].do_tool_retrieval, do_filter=False, do_use_self_memory=False) - self.global_memory = Memory([]) - self.local_memory = Memory([]) - self.do_code_exec = do_code_exec - self.codebox = PyCodeBox( - remote_url=SANDBOX_SERVER["url"], - remote_ip=SANDBOX_SERVER["host"], - remote_port=SANDBOX_SERVER["port"], - token="mytoken", - do_code_exe=True, - do_remote=SANDBOX_SERVER["do_remote"], - do_check_net=False - ) + self.do_agent_selector = False + self.agent_selector = CheckAgent(role=role_configs["checker"].role, + task = None, + memory = None, + do_search = role_configs["checker"].do_search, + do_doc_retrieval = role_configs["checker"].do_doc_retrieval, + do_tool_retrieval = role_configs["checker"].do_tool_retrieval, + do_filter=False, do_use_self_memory=False) + + self.messageUtils = MessageUtils() + # all memory created by agent until instance deleted + self.global_memory = Memory(messages=[]) + # self.do_code_exec = do_code_exec + # self.codebox = PyCodeBox( + # remote_url=SANDBOX_SERVER["url"], + # remote_ip=SANDBOX_SERVER["host"], + # remote_port=SANDBOX_SERVER["port"], + # token="mytoken", + # do_code_exe=True, + # do_remote=SANDBOX_SERVER["do_remote"], + # do_check_net=False + # ) - def step(self, query: Message, history: Memory = None, background: Memory = None) -> Message: + def step(self, query: Message, history: Memory = None, background: Memory = None, memory_pool: Memory = None) -> Message: '''execute chain''' - local_memory = Memory([]) + for output_message, local_memory in self.astep(query, history, background, memory_pool): + pass + return output_message, local_memory + + def astep(self, query: Message, history: Memory = None, background: Memory = None, memory_pool: Memory = None) -> Message: + '''execute chain''' + local_memory = Memory(messages=[]) input_message = copy.deepcopy(query) step_nums = copy.deepcopy(self.chat_turn) check_message = None self.global_memory.append(input_message) - local_memory.append(input_message) + # local_memory.append(input_message) while step_nums > 0: - for agent in self.agents: - output_message = agent.run(input_message, history, background=background) - output_message = self.inherit_extrainfo(input_message, output_message) + if self.do_agent_selector: + agent_message = copy.deepcopy(query) + agent_message.agents = self.agents + for selectory_message in self.agent_selector.arun(query, background=self.global_memory, memory_pool=memory_pool): + pass + selectory_message = self.messageUtils.parser(selectory_message) + selectory_message = self.messageUtils.filter(selectory_message) + agent = self.agents[selectory_message.agent_index] + # selector agent to execure next task + for output_message in agent.arun(input_message, history, background=background, memory_pool=memory_pool): + # 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 - logger.info(f"{agent.role.role_name} message: {output_message.role_content}") - output_message = self.parser(output_message) - # output_message = self.step_router(output_message) + # logger.info(f"{agent.role.role_name}\nmessage: {output_message.step_content}\nquery: {output_message.input_query}") + output_message = self.messageUtils.parser(output_message) + yield output_message, local_memory + output_message input_message = output_message self.global_memory.append(output_message) @@ -84,192 +112,243 @@ class BaseChain: # when get finished signal can stop early if output_message.action_status == ActionStatus.FINISHED: break + else: + for agent in self.agents: + for output_message in agent.arun(input_message, history, background=background, memory_pool=memory_pool): + # 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 + # logger.info(f"{agent.role.role_name} currenct message: {output_message.step_content}\n next llm question: {output_message.input_query}") + output_message = self.messageUtils.parser(output_message) + yield output_message, local_memory + output_message + # output_message = self.step_router(output_message) - if self.do_checker: - logger.debug(f"{self.checker.role.role_name} input global memory: {self.global_memory.to_str_messages(content_key='step_content')}") - check_message = self.checker.run(query, background=self.global_memory) - check_message = self.parser(check_message) - check_message = self.filter(check_message) - check_message = self.inherit_extrainfo(output_message, check_message) - logger.debug(f"{self.checker.role.role_name}: {check_message.role_content}") + input_message = output_message + self.global_memory.append(output_message) - if check_message.action_status == ActionStatus.FINISHED: - self.global_memory.append(check_message) - break + local_memory.append(output_message) + # when get finished signal can stop early + if output_message.action_status == ActionStatus.FINISHED: + action_status = False + break + + if self.do_checker and self.chat_turn > 1: + # logger.debug(f"{self.checker.role.role_name} input global memory: {self.global_memory.to_str_messages(content_key='step_content', return_all=False)}") + for check_message in self.checker.arun(query, background=local_memory, memory_pool=memory_pool): + pass + check_message = self.messageUtils.parser(check_message) + check_message = self.messageUtils.filter(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 - - return check_message or output_message, local_memory - - def step_router(self, message: Message) -> Message: - '''''' - # message = self.parser(message) - # logger.debug(f"message.action_status: {message.action_status}") - if message.action_status == ActionStatus.CODING: - message = self.code_step(message) - elif message.action_status == ActionStatus.TOOL_USING: - message = self.tool_step(message) - - return 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)) - 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观察: 执行代码后获得输出一张图片, 文件名为{uid}\n" - message.observation = f"\n观察: 执行代码后获得输出一张图片, 文件名为{uid}\n" - message.step_content += f"\n观察: 执行代码后获得输出一张图片, 文件名为{uid}\n" - message.step_contents += [f"\n观察: 执行代码后获得输出一张图片, 文件名为{uid}\n"] - message.role_content += f"\n执行代码后获得输出一张图片, 文件名为{uid}\n" - else: - message.code_answer = code_answer.code_exe_response - message.observation = code_answer.code_exe_response - message.step_content += f"\n观察: 执行代码后获得输出是 {code_answer.code_exe_response}\n" - message.step_contents += [f"\n观察: 执行代码后获得输出是 {code_answer.code_exe_response}\n"] - message.role_content += f"\n观察: 执行代码后获得输出是 {code_answer.code_exe_response}\n" - logger.info(f"观察: {message.action_status}, {message.observation}") - return message - - def tool_step(self, message: Message) -> Message: - '''execute tool''' - # logger.debug(f"message: {message.action_status}, {message.tool_name}, {message.tool_params}") - tool_names = [tool.name for tool in message.tools] - if message.tool_name not in tool_names: - message.tool_answer = "不存在可以执行的tool" - message.observation = "不存在可以执行的tool" - message.role_content += f"\n观察: 不存在可以执行的tool\n" - message.step_content += f"\n观察: 不存在可以执行的tool\n" - message.step_contents += [f"\n观察: 不存在可以执行的tool\n"] - for tool in message.tools: - if tool.name == message.tool_name: - tool_res = tool.func(**message.tool_params) - message.tool_answer = tool_res - message.observation = tool_res - message.role_content += f"\n观察: {tool_res}\n" - message.step_content += f"\n观察: {tool_res}\n" - message.step_contents += [f"\n观察: {tool_res}\n"] - return message - - def filter(self, message: Message, stop=None) -> Message: - - tool_params = self.parser_spec_key(message.role_content, "tool_params") - code_content = self.parser_spec_key(message.role_content, "code_content") - plan = self.parser_spec_key(message.role_content, "plan") - plans = self.parser_spec_key(message.role_content, "plans", do_search=False) - content = self.parser_spec_key(message.role_content, "content", do_search=False) - - # logger.debug(f"tool_params: {tool_params}, code_content: {code_content}, plan: {plan}, plans: {plans}, content: {content}") - role_content = tool_params or code_content or plan or plans or content - message.role_content = role_content or message.role_content - return message - - def parser(self, message: Message) -> Message: - '''''' - content = message.role_content - parser_keys = ["action", "code_content", "code_filename", "tool_params", "plans"] - try: - s_json = self._parse_json(content) - message.action_status = s_json.get("action") - message.code_content = s_json.get("code_content") - message.tool_params = s_json.get("tool_params") - message.tool_name = s_json.get("tool_name") - message.code_filename = s_json.get("code_filename") - message.plans = s_json.get("plans") - # for parser_key in parser_keys: - # message.action_status = content.get(parser_key) - except Exception as e: - # logger.warning(f"{traceback.format_exc()}") - action_value = self._match(r"'action':\s*'([^']*)'", content) if "'action'" in content else self._match(r'"action":\s*"([^"]*)"', content) - code_content_value = self._match(r"'code_content':\s*'([^']*)'", content) if "'code_content'" in content else self._match(r'"code_content":\s*"([^"]*)"', content) - filename_value = self._match(r"'code_filename':\s*'([^']*)'", content) if "'code_filename'" in content else self._match(r'"code_filename":\s*"([^"]*)"', content) - tool_params_value = self._match(r"'tool_params':\s*(\{[^{}]*\})", content, do_json=True) if "'tool_params'" in content \ - else self._match(r'"tool_params":\s*(\{[^{}]*\})', content, do_json=True) - tool_name_value = self._match(r"'tool_name':\s*'([^']*)'", content) if "'tool_name'" in content else self._match(r'"tool_name":\s*"([^"]*)"', content) - plans_value = self._match(r"'plans':\s*(\[.*?\])", content, do_search=False) if "'plans'" in content else self._match(r'"plans":\s*(\[.*?\])', content, do_search=False, ) - # re解析 - message.action_status = action_value or "default" - message.code_content = code_content_value - message.code_filename = filename_value - message.tool_params = tool_params_value - message.tool_name = tool_name_value - message.plans = plans_value - - logger.debug(f"确认当前的action: {message.action_status}") - - return message - - def parser_spec_key(self, content, key, do_search=True, do_json=False) -> str: - '''''' - key2pattern = { - "'action'": r"'action':\s*'([^']*)'", '"action"': r'"action":\s*"([^"]*)"', - "'code_content'": r"'code_content':\s*'([^']*)'", '"code_content"': r'"code_content":\s*"([^"]*)"', - "'code_filename'": r"'code_filename':\s*'([^']*)'", '"code_filename"': r'"code_filename":\s*"([^"]*)"', - "'tool_params'": r"'tool_params':\s*(\{[^{}]*\})", '"tool_params"': r'"tool_params":\s*(\{[^{}]*\})', - "'tool_name'": r"'tool_name':\s*'([^']*)'", '"tool_name"': r'"tool_name":\s*"([^"]*)"', - "'plans'": r"'plans':\s*(\[.*?\])", '"plans"': r'"plans":\s*(\[.*?\])', - "'content'": r"'content':\s*'([^']*)'", '"content"': r'"content":\s*"([^"]*)"', - } + # + output_message = check_message or output_message # 返回chain和checker的结果 + output_message.input_query = query.input_query # chain和chain之间消息通信不改变问题 + yield output_message, local_memory - s_json = self._parse_json(content) - try: - if s_json and key in s_json: - return str(s_json[key]) - except: - pass + # def step_router(self, message: Message) -> Message: + # '''''' + # # message = self.parser(message) + # # logger.debug(f"message.action_status: {message.action_status}") + # if message.action_status == ActionStatus.CODING: + # message = self.code_step(message) + # elif message.action_status == ActionStatus.TOOL_USING: + # message = self.tool_step(message) - keystr = f"'{key}'" if f"'{key}'" in content else f'"{key}"' - return self._match(key2pattern.get(keystr, fr"'{key}':\s*'([^']*)'"), content, do_search=do_search, do_json=do_json) - - def _match(self, pattern, s, do_search=True, do_json=False): - try: - if do_search: - match = re.search(pattern, s) - if match: - value = match.group(1).replace("\\n", "\n") - if do_json: - value = json.loads(value) - else: - value = None - else: - match = re.findall(pattern, s, re.DOTALL) - if match: - value = match[0] - if do_json: - value = json.loads(value) - else: - value = None - except Exception as e: - logger.warning(f"{traceback.format_exc()}") + # return message - # logger.debug(f"pattern: {pattern}, s: {s}, match: {match}") - return value - - def _parse_json(self, s): - try: - pattern = r"```([^`]+)```" - match = re.findall(pattern, s) - if match: - return eval(match[0]) - except: - pass - return None + # 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)) + # 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观察: 执行代码后获得输出一张图片, 文件名为{uid}\n" + # message.observation = f"\n观察: 执行代码后获得输出一张图片, 文件名为{uid}\n" + # message.step_content += f"\n观察: 执行代码后获得输出一张图片, 文件名为{uid}\n" + # message.step_contents += [f"\n观察: 执行代码后获得输出一张图片, 文件名为{uid}\n"] + # message.role_content += f"\n执行代码后获得输出一张图片, 文件名为{uid}\n" + # else: + # message.code_answer = code_answer.code_exe_response + # message.observation = code_answer.code_exe_response + # message.step_content += f"\n观察: 执行代码后获得输出是 {code_answer.code_exe_response}\n" + # message.step_contents += [f"\n观察: 执行代码后获得输出是 {code_answer.code_exe_response}\n"] + # message.role_content += f"\n观察: 执行代码后获得输出是 {code_answer.code_exe_response}\n" + # logger.info(f"观察: {message.action_status}, {message.observation}") + # return message - def inherit_extrainfo(self, input_message: Message, output_message: Message): - 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) - return output_message + # def tool_step(self, message: Message) -> Message: + # '''execute tool''' + # # logger.debug(f"message: {message.action_status}, {message.tool_name}, {message.tool_params}") + # tool_names = [tool.name for tool in message.tools] + # if message.tool_name not in tool_names: + # message.tool_answer = "不存在可以执行的tool" + # message.observation = "不存在可以执行的tool" + # message.role_content += f"\n观察: 不存在可以执行的tool\n" + # message.step_content += f"\n观察: 不存在可以执行的tool\n" + # message.step_contents += [f"\n观察: 不存在可以执行的tool\n"] + # for tool in message.tools: + # if tool.name == message.tool_name: + # tool_res = tool.func(**message.tool_params) + # message.tool_answer = tool_res + # message.observation = tool_res + # message.role_content += f"\n观察: {tool_res}\n" + # message.step_content += f"\n观察: {tool_res}\n" + # message.step_contents += [f"\n观察: {tool_res}\n"] + # return message + + # def filter(self, message: Message, stop=None) -> Message: + + # tool_params = self.parser_spec_key(message.role_content, "tool_params") + # code_content = self.parser_spec_key(message.role_content, "code_content") + # plan = self.parser_spec_key(message.role_content, "plan") + # plans = self.parser_spec_key(message.role_content, "plans", do_search=False) + # content = self.parser_spec_key(message.role_content, "content", do_search=False) + + # # logger.debug(f"tool_params: {tool_params}, code_content: {code_content}, plan: {plan}, plans: {plans}, content: {content}") + # role_content = tool_params or code_content or plan or plans or content + # message.role_content = role_content or message.role_content + # return message - def get_memory(self, do_all_memory=True, content_key="role_content") -> Memory: - memory = self.global_memory if do_all_memory else self.local_memory + # def parser(self, message: Message) -> Message: + # '''''' + # content = message.role_content + # parser_keys = ["action", "code_content", "code_filename", "tool_params", "plans"] + # try: + # s_json = self._parse_json(content) + # message.action_status = s_json.get("action") + # message.code_content = s_json.get("code_content") + # message.tool_params = s_json.get("tool_params") + # message.tool_name = s_json.get("tool_name") + # message.code_filename = s_json.get("code_filename") + # message.plans = s_json.get("plans") + # # for parser_key in parser_keys: + # # message.action_status = content.get(parser_key) + # except Exception as e: + # # logger.warning(f"{traceback.format_exc()}") + # def parse_text_to_dict(text): + # # Define a regular expression pattern to capture the key and value + # main_pattern = r"\*\*(.+?):\*\*\s*(.*?)\s*(?=\*\*|$)" + # code_pattern = r'```python\n(.*?)```' + + # # 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} + + # # Search for the code block + # code_match = re.search(code_pattern, text, re.DOTALL) + # if code_match: + # # Add the code block to the dictionary + # parsed_dict['code'] = code_match.group(1).strip() + + # return parsed_dict + + # parsed_dict = parse_text_to_dict(content) + # action_value = parsed_dict.get('Action Status') + # if action_value: + # action_value = action_value.lower() + # logger.debug(f'action_value: {action_value}') + # # action_value = self._match(r"'action':\s*'([^']*)'", content) if "'action'" in content else self._match(r'"action":\s*"([^"]*)"', content) + + # code_content_value = parsed_dict.get('code') + # # code_content_value = self._match(r"'code_content':\s*'([^']*)'", content) if "'code_content'" in content else self._match(r'"code_content":\s*"([^"]*)"', content) + # filename_value = self._match(r"'code_filename':\s*'([^']*)'", content) if "'code_filename'" in content else self._match(r'"code_filename":\s*"([^"]*)"', content) + # tool_params_value = self._match(r"'tool_params':\s*(\{[^{}]*\})", content, do_json=True) if "'tool_params'" in content \ + # else self._match(r'"tool_params":\s*(\{[^{}]*\})', content, do_json=True) + # tool_name_value = self._match(r"'tool_name':\s*'([^']*)'", content) if "'tool_name'" in content else self._match(r'"tool_name":\s*"([^"]*)"', content) + # plans_value = self._match(r"'plans':\s*(\[.*?\])", content, do_search=False) if "'plans'" in content else self._match(r'"plans":\s*(\[.*?\])', content, do_search=False, ) + # # re解析 + # message.action_status = action_value or "default" + # message.code_content = code_content_value + # message.code_filename = filename_value + # message.tool_params = tool_params_value + # message.tool_name = tool_name_value + # message.plans = plans_value + + # # logger.debug(f"确认当前的action: {message.action_status}") + + # return message + + # def parser_spec_key(self, content, key, do_search=True, do_json=False) -> str: + # '''''' + # key2pattern = { + # "'action'": r"'action':\s*'([^']*)'", '"action"': r'"action":\s*"([^"]*)"', + # "'code_content'": r"'code_content':\s*'([^']*)'", '"code_content"': r'"code_content":\s*"([^"]*)"', + # "'code_filename'": r"'code_filename':\s*'([^']*)'", '"code_filename"': r'"code_filename":\s*"([^"]*)"', + # "'tool_params'": r"'tool_params':\s*(\{[^{}]*\})", '"tool_params"': r'"tool_params":\s*(\{[^{}]*\})', + # "'tool_name'": r"'tool_name':\s*'([^']*)'", '"tool_name"': r'"tool_name":\s*"([^"]*)"', + # "'plans'": r"'plans':\s*(\[.*?\])", '"plans"': r'"plans":\s*(\[.*?\])', + # "'content'": r"'content':\s*'([^']*)'", '"content"': r'"content":\s*"([^"]*)"', + # } + + # s_json = self._parse_json(content) + # try: + # if s_json and key in s_json: + # return str(s_json[key]) + # except: + # pass + + # keystr = f"'{key}'" if f"'{key}'" in content else f'"{key}"' + # return self._match(key2pattern.get(keystr, fr"'{key}':\s*'([^']*)'"), content, do_search=do_search, do_json=do_json) + + # def _match(self, pattern, s, do_search=True, do_json=False): + # try: + # if do_search: + # match = re.search(pattern, s) + # if match: + # value = match.group(1).replace("\\n", "\n") + # if do_json: + # value = json.loads(value) + # else: + # value = None + # else: + # match = re.findall(pattern, s, re.DOTALL) + # if match: + # value = match[0] + # if do_json: + # value = json.loads(value) + # else: + # value = None + # except Exception as e: + # logger.warning(f"{traceback.format_exc()}") + + # # logger.debug(f"pattern: {pattern}, s: {s}, match: {match}") + # return value + + # def _parse_json(self, s): + # try: + # pattern = r"```([^`]+)```" + # match = re.findall(pattern, s) + # if match: + # return eval(match[0]) + # except: + # pass + # return None + + # def inherit_extrainfo(self, input_message: Message, output_message: Message): + # 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.origin_query = input_message.origin_query + # output_message.figures.update(input_message.figures) + # return output_message + + 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, do_all_memory=True, content_key="role_content") -> Memory: - memory = self.global_memory if do_all_memory else self.local_memory + def get_memory_str(self, content_key="role_content") -> Memory: + memory = self.global_memory # for i in memory.to_tuple_messages(content_key=content_key): # logger.debug(f"{i}") return "\n".join([": ".join(i) for i in memory.to_tuple_messages(content_key=content_key)]) diff --git a/dev_opsgpt/connector/chains/chains.py b/dev_opsgpt/connector/chains/chains.py index fd490b0..a8536e6 100644 --- a/dev_opsgpt/connector/chains/chains.py +++ b/dev_opsgpt/connector/chains/chains.py @@ -1,28 +1,18 @@ from typing import List +from loguru import logger +import copy from dev_opsgpt.connector.agents import BaseAgent from .base_chain import BaseChain +from dev_opsgpt.connector.agents import BaseAgent, CheckAgent +from dev_opsgpt.connector.schema import ( + Memory, Role, Message, ActionStatus, ChainConfig, + load_role_configs +) -class simpleChatChain(BaseChain): - - def __init__(self, agents: List[BaseAgent], do_code_exec: bool = False) -> None: - super().__init__(agents, do_code_exec) - - -class toolChatChain(BaseChain): - - def __init__(self, agents: List[BaseAgent], do_code_exec: bool = False) -> None: - super().__init__(agents, do_code_exec) - - -class dataAnalystChain(BaseChain): - - def __init__(self, agents: List[BaseAgent], do_code_exec: bool = False) -> None: - super().__init__(agents, do_code_exec) - - -class plannerChain(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/dev_opsgpt/connector/configs/agent_config.py b/dev_opsgpt/connector/configs/agent_config.py index dc52852..66bf623 100644 --- a/dev_opsgpt/connector/configs/agent_config.py +++ b/dev_opsgpt/connector/configs/agent_config.py @@ -1,213 +1,33 @@ from enum import Enum +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, + 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 +) + class AgentType: REACT = "ReactAgent" + EXECUTOR = "ExecutorAgent" ONE_STEP = "BaseAgent" DEFAULT = "BaseAgent" -REACT_TOOL_PROMPT = """尽可能地以有帮助和准确的方式回应人类。您可以使用以下工具: -{formatted_tools} -使用json blob来指定一个工具,提供一个action关键字(工具名称)和一个tool_params关键字(工具输入)。 -有效的"action"值为:"finished" 或 "tool_using" (使用工具来回答问题) -有效的"tool_name"值为:{tool_names} -请仅在每个$JSON_BLOB中提供一个action,如下所示: -``` -{{{{ -"action": $ACTION, -"tool_name": $TOOL_NAME -"tool_params": $INPUT -}}}} -``` - -按照以下格式进行回应: -问题:输入问题以回答 -思考:考虑之前和之后的步骤 -行动: -``` -$JSON_BLOB -``` -观察:行动结果 -...(重复思考/行动/观察N次) -思考:我知道该如何回应 -行动: -``` -{{{{ -"action": "finished", -"tool_name": "notool" -"tool_params": "最终返回答案给到用户" -}}}} -``` -""" - -REACT_PROMPT_INPUT = '''下面开始!记住根据问题进行返回需要生成的答案 -问题: {query}''' - - -REACT_CODE_PROMPT = """尽可能地以有帮助和准确的方式回应人类,能够逐步编写可执行并打印变量的代码来解决问题 -使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)和一个 code (生成代码)。 -有效的 'action' 值为:'coding'(结合总结下述思维链过程编写下一步的可执行代码) or 'finished' (总结下述思维链过程可回答问题)。 -在每个 $JSON_BLOB 中仅提供一个 action,如下所示: -``` -{{{{'action': $ACTION,'code_content': $CODE}}}} -``` - -按照以下思维链格式进行回应: -问题:输入问题以回答 -思考:考虑之前和之后的步骤 -行动: -``` -$JSON_BLOB -``` -观察:行动结果 -...(重复思考/行动/观察N次) -思考:我知道该如何回应 -行动: -``` -{{{{ -"action": "finished", -"code_content": "总结上述思维链过程回答问题" -}}}} -``` -""" - -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 -``` -""" - - -RECOGNIZE_INTENTION_PROMPT = """你是一个任务决策助手,能够将理解用户意图并决策采取最合适的行动,尽可能地以有帮助和准确的方式回应人类, -使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)。 -有效的 'action' 值为:'planning'(需要先进行拆解计划) or 'only_answer' (不需要拆解问题即可直接回答问题)or "tool_using" (使用工具来回答问题) or 'coding'(生成可执行的代码)。 -在每个 $JSON_BLOB 中仅提供一个 action,如下所示: -``` -{{'action': $ACTION}} -``` -按照以下格式进行回应: -问题:输入问题以回答 -行动:$ACTION -``` -$JSON_BLOB -``` -""" - - -CHECKER_PROMPT = """尽可能地以有帮助和准确的方式回应人类,判断问题是否得到解答,同时展现解答的过程和内容 -使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)。 -有效的 'action' 值为:'finished'(任务已经可以通过“背景信息”和“对话信息”回答问题) or 'continue' (“背景信息”和“对话信息”不足以回答问题)。 -在每个 $JSON_BLOB 中仅提供一个 action,如下所示: -``` -{{'action': $ACTION, 'content': '提取“背景信息”和“对话信息”中信息来回答问题'}} -``` -按照以下格式进行回应: -问题:输入问题以回答 -行动:$ACTION -``` -$JSON_BLOB -``` -""" - -CONV_SUMMARY_PROMPT = """尽可能地以有帮助和准确的方式回应人类,根据“背景信息”中的有效信息回答问题, -使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)。 -有效的 'action' 值为:'finished'(任务已经可以通过上下文信息可以回答) or 'continue' (根据背景信息回答问题)。 -在每个 $JSON_BLOB 中仅提供一个 action,如下所示: -``` -{{'action': $ACTION, 'content': '根据背景信息回答问题'}} -``` -按照以下格式进行回应: -问题:输入问题以回答 -行动: -``` -$JSON_BLOB -``` -""" - -CONV_SUMMARY_PROMPT = """尽可能地以有帮助和准确的方式回应人类 -根据“背景信息”中的有效信息回答问题,同时展现解答的过程和内容 -若能根“背景信息”回答问题,则直接回答 -否则,总结“背景信息”的内容 -""" - - - -QA_PROMPT = """根据已知信息,简洁和专业的来回答问题。如果无法从中得到答案,请说 “根据已知信息无法回答该问题”,不允许在答案中添加编造成分,答案请使用中文。 -使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)。 -有效的 'action' 值为:'finished'(任务已经可以通过上下文信息可以回答) or 'continue' (上下文信息不足以回答问题)。 -在每个 $JSON_BLOB 中仅提供一个 action,如下所示: -``` -{{'action': $ACTION, 'content': '总结对话内容'}} -``` -按照以下格式进行回应: -问题:输入问题以回答 -行动:$ACTION -``` -$JSON_BLOB -``` -""" - -CODE_QA_PROMPT = """【指令】根据已知信息来回答问题""" - AGETN_CONFIGS = { "checker": { "role": { - "role_prompt": CHECKER_PROMPT, - "role_type": "ai", + "role_prompt": CHECKER_TEMPLATE_PROMPT, + "role_type": "assistant", "role_name": "checker", "role_desc": "", "agent_type": "BaseAgent" @@ -220,7 +40,7 @@ AGETN_CONFIGS = { "conv_summary": { "role": { "role_prompt": CONV_SUMMARY_PROMPT, - "role_type": "ai", + "role_type": "assistant", "role_name": "conv_summary", "role_desc": "", "agent_type": "BaseAgent" @@ -232,8 +52,8 @@ AGETN_CONFIGS = { }, "general_planner": { "role": { - "role_prompt": GENERAL_PLANNER_PROMPT, - "role_type": "ai", + "role_prompt": PLANNER_TEMPLATE_PROMPT, + "role_type": "assistant", "role_name": "general_planner", "role_desc": "", "agent_type": "BaseAgent" @@ -243,10 +63,37 @@ AGETN_CONFIGS = { "do_doc_retrieval": False, "do_tool_retrieval": False }, + "executor": { + "role": { + "role_prompt": EXECUTOR_TEMPLATE_PROMPT, + "role_type": "assistant", + "role_name": "executor", + "role_desc": "", + "agent_type": "ExecutorAgent", + }, + "stop": "\n**Observation:**", + "chat_turn": 1, + "do_search": False, + "do_doc_retrieval": False, + "do_tool_retrieval": False + }, + "base_refiner": { + "role": { + "role_prompt": REFINE_TEMPLATE_PROMPT, + "role_type": "assistant", + "role_name": "base_refiner", + "role_desc": "", + "agent_type": "BaseAgent" + }, + "chat_turn": 1, + "do_search": False, + "do_doc_retrieval": False, + "do_tool_retrieval": False + }, "planner": { "role": { "role_prompt": DATA_PLANNER_PROMPT, - "role_type": "ai", + "role_type": "assistant", "role_name": "planner", "role_desc": "", "agent_type": "BaseAgent" @@ -259,7 +106,7 @@ AGETN_CONFIGS = { "intention_recognizer": { "role": { "role_prompt": RECOGNIZE_INTENTION_PROMPT, - "role_type": "ai", + "role_type": "assistant", "role_name": "intention_recognizer", "role_desc": "", "agent_type": "BaseAgent" @@ -272,7 +119,7 @@ AGETN_CONFIGS = { "tool_planner": { "role": { "role_prompt": TOOL_PLANNER_PROMPT, - "role_type": "ai", + "role_type": "assistant", "role_name": "tool_planner", "role_desc": "", "agent_type": "BaseAgent" @@ -282,10 +129,37 @@ AGETN_CONFIGS = { "do_doc_retrieval": False, "do_tool_retrieval": False }, + "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", + }, + "stop": "\n**Observation:**", + "chat_turn": 7, + "do_search": False, + "do_doc_retrieval": False, + "do_tool_retrieval": False + }, + "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" + }, + "chat_turn": 1, + "do_search": False, + "do_doc_retrieval": False, + "do_tool_retrieval": False + }, "tool_react": { "role": { "role_prompt": REACT_TOOL_PROMPT, - "role_type": "ai", + "role_type": "assistant", "role_name": "tool_react", "role_desc": "", "agent_type": "ReactAgent" @@ -294,12 +168,12 @@ AGETN_CONFIGS = { "do_search": False, "do_doc_retrieval": False, "do_tool_retrieval": False, - "stop": "观察" + "stop": "\n**Observation:**" }, "code_react": { "role": { "role_prompt": REACT_CODE_PROMPT, - "role_type": "ai", + "role_type": "assistant", "role_name": "code_react", "role_desc": "", "agent_type": "ReactAgent" @@ -308,25 +182,25 @@ AGETN_CONFIGS = { "do_search": False, "do_doc_retrieval": False, "do_tool_retrieval": False, - "stop": "观察" + "stop": "\n**Observation:**" }, "qaer": { "role": { - "role_prompt": QA_PROMPT, - "role_type": "ai", + "role_prompt": QA_TEMPLATE_PROMPT, + "role_type": "assistant", "role_name": "qaer", "role_desc": "", "agent_type": "BaseAgent" }, "chat_turn": 1, "do_search": False, - "do_doc_retrieval": True, + "do_doc_retrieval": False, "do_tool_retrieval": False }, "code_qaer": { "role": { "role_prompt": CODE_QA_PROMPT , - "role_type": "ai", + "role_type": "assistant", "role_name": "code_qaer", "role_desc": "", "agent_type": "BaseAgent" @@ -338,8 +212,8 @@ AGETN_CONFIGS = { }, "searcher": { "role": { - "role_prompt": QA_PROMPT, - "role_type": "ai", + "role_prompt": QA_TEMPLATE_PROMPT, + "role_type": "assistant", "role_name": "searcher", "role_desc": "", "agent_type": "BaseAgent" @@ -349,62 +223,65 @@ AGETN_CONFIGS = { "do_doc_retrieval": False, "do_tool_retrieval": False }, - "answer": { + "metaGPT_PRD": { "role": { - "role_prompt": "", - "role_type": "ai", - "role_name": "answer", + "role_prompt": PRD_WRITER_METAGPT_PROMPT, + "role_type": "assistant", + "role_name": "metaGPT_PRD", "role_desc": "", "agent_type": "BaseAgent" }, "chat_turn": 1, "do_search": False, "do_doc_retrieval": False, - "do_tool_retrieval": False + "do_tool_retrieval": False, + "focus_agents": [], + "focus_message_keys": [], }, - "data_analyst": { + + "metaGPT_DESIGN": { "role": { - "role_prompt": """你是一个数据分析的代码开发助手,能够编写可执行的代码来完成相关的数据分析问题,使用 JSON Blob 来指定一个返回的内容,通过提供一个 action(行动)和一个 code (生成代码)和 一个 file_name (指定保存文件)。\ - 有效的 'action' 值为:'coding'(生成可执行的代码) or 'finished' (不生成代码并直接返回答案)。在每个 $JSON_BLOB 中仅提供一个 action,如下所示:\ - ```\n{{'action': $ACTION,'code_content': $CODE, 'code_filename': $FILE_NAME}}```\ - 下面开始!记住根据问题进行返回需要生成的答案,格式为 ```JSON_BLOB```""", - "role_type": "ai", - "role_name": "data_analyst", + "role_prompt": DESIGN_WRITER_METAGPT_PROMPT, + "role_type": "assistant", + "role_name": "metaGPT_DESIGN", "role_desc": "", "agent_type": "BaseAgent" }, "chat_turn": 1, "do_search": False, "do_doc_retrieval": False, - "do_tool_retrieval": False + "do_tool_retrieval": False, + "focus_agents": ["metaGPT_PRD"], + "focus_message_keys": [], }, - "deveploer": { + "metaGPT_TASK": { "role": { - "role_prompt": """你是一个代码开发助手,能够编写可执行的代码来完成问题,使用 JSON Blob 来指定一个返回的内容,通过提供一个 action(行动)和一个 code (生成代码)和 一个 file_name (指定保存文件)。\ - 有效的 'action' 值为:'coding'(生成可执行的代码) or 'finished' (不生成代码并直接返回答案)。在每个 $JSON_BLOB 中仅提供一个 action,如下所示:\ - ```\n{{'action': $ACTION,'code_content': $CODE, 'code_filename': $FILE_NAME}}```\ - 下面开始!记住根据问题进行返回需要生成的答案,格式为 ```JSON_BLOB```""", - "role_type": "ai", - "role_name": "deveploer", + "role_prompt": TASK_WRITER_METAGPT_PROMPT, + "role_type": "assistant", + "role_name": "metaGPT_TASK", "role_desc": "", "agent_type": "BaseAgent" }, "chat_turn": 1, "do_search": False, "do_doc_retrieval": False, - "do_tool_retrieval": False + "do_tool_retrieval": False, + "focus_agents": ["metaGPT_DESIGN"], + "focus_message_keys": [], }, - "tester": { + "metaGPT_CODER": { "role": { - "role_prompt": "你是一个QA问答的助手,能够尽可能准确地回答问题,下面请逐步思考问题并回答", - "role_type": "ai", - "role_name": "tester", + "role_prompt": CODE_WRITER_METAGPT_PROMPT, + "role_type": "assistant", + "role_name": "metaGPT_CODER", "role_desc": "", - "agent_type": "BaseAgent" + "agent_type": "ExecutorAgent" }, "chat_turn": 1, "do_search": False, "do_doc_retrieval": False, - "do_tool_retrieval": False - } + "do_tool_retrieval": False, + "focus_agents": ["metaGPT_DESIGN", "metaGPT_TASK"], + "focus_message_keys": [], + }, } \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/agent_prompt/design_writer.yaml b/dev_opsgpt/connector/configs/agent_prompt/design_writer.yaml new file mode 100644 index 0000000..e1135ef --- /dev/null +++ b/dev_opsgpt/connector/configs/agent_prompt/design_writer.yaml @@ -0,0 +1,99 @@ +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/dev_opsgpt/connector/configs/agent_prompt/prd_writer.yaml b/dev_opsgpt/connector/configs/agent_prompt/prd_writer.yaml new file mode 100644 index 0000000..0aec402 --- /dev/null +++ b/dev_opsgpt/connector/configs/agent_prompt/prd_writer.yaml @@ -0,0 +1,101 @@ +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/dev_opsgpt/connector/configs/agent_prompt/review_code.yaml b/dev_opsgpt/connector/configs/agent_prompt/review_code.yaml new file mode 100644 index 0000000..32567d8 --- /dev/null +++ b/dev_opsgpt/connector/configs/agent_prompt/review_code.yaml @@ -0,0 +1,177 @@ + +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/dev_opsgpt/connector/configs/agent_prompt/task_write.yaml b/dev_opsgpt/connector/configs/agent_prompt/task_write.yaml new file mode 100644 index 0000000..faf2c2c --- /dev/null +++ b/dev_opsgpt/connector/configs/agent_prompt/task_write.yaml @@ -0,0 +1,148 @@ +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/dev_opsgpt/connector/configs/agent_prompt/write_code.yaml b/dev_opsgpt/connector/configs/agent_prompt/write_code.yaml new file mode 100644 index 0000000..4193b8b --- /dev/null +++ b/dev_opsgpt/connector/configs/agent_prompt/write_code.yaml @@ -0,0 +1,147 @@ +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/dev_opsgpt/connector/configs/chain_config.py b/dev_opsgpt/connector/configs/chain_config.py index 5ed2605..bc43b8d 100644 --- a/dev_opsgpt/connector/configs/chain_config.py +++ b/dev_opsgpt/connector/configs/chain_config.py @@ -1,17 +1,16 @@ +from enum import Enum +# from .prompts import PLANNER_TEMPLATE_PROMPT + CHAIN_CONFIGS = { "chatChain": { "chain_name": "chatChain", "chain_type": "BaseChain", - "agents": ["answer"], + "agents": ["qaer"], "chat_turn": 1, "do_checker": False, - "clear_structure": "True", - "brainstorming": "False", - "gui_design": "True", - "git_management": "False", - "self_improve": "False" + "chain_prompt": "" }, "docChatChain": { "chain_name": "docChatChain", @@ -19,11 +18,7 @@ CHAIN_CONFIGS = { "agents": ["qaer"], "chat_turn": 1, "do_checker": False, - "clear_structure": "True", - "brainstorming": "False", - "gui_design": "True", - "git_management": "False", - "self_improve": "False" + "chain_prompt": "" }, "searchChatChain": { "chain_name": "searchChatChain", @@ -31,11 +26,7 @@ CHAIN_CONFIGS = { "agents": ["searcher"], "chat_turn": 1, "do_checker": False, - "clear_structure": "True", - "brainstorming": "False", - "gui_design": "True", - "git_management": "False", - "self_improve": "False" + "chain_prompt": "" }, "codeChatChain": { "chain_name": "codehChatChain", @@ -43,11 +34,7 @@ CHAIN_CONFIGS = { "agents": ["code_qaer"], "chat_turn": 1, "do_checker": False, - "clear_structure": "True", - "brainstorming": "False", - "gui_design": "True", - "git_management": "False", - "self_improve": "False" + "chain_prompt": "" }, "toolReactChain": { "chain_name": "toolReactChain", @@ -55,34 +42,70 @@ CHAIN_CONFIGS = { "agents": ["tool_planner", "tool_react"], "chat_turn": 2, "do_checker": True, - "clear_structure": "True", - "brainstorming": "False", - "gui_design": "True", - "git_management": "False", - "self_improve": "False" + "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": ["planner", "code_react"], - "chat_turn": 2, + "agents": ["code_react"], + "chat_turn": 6, "do_checker": True, - "clear_structure": "True", - "brainstorming": "False", - "gui_design": "True", - "git_management": "False", - "self_improve": "False" + "chain_prompt": "" }, - "dataAnalystChain": { - "chain_name": "dataAnalystChain", + "codeToolPlanChain": { + "chain_name": "codeToolPlanChain", "chain_type": "BaseChain", - "agents": ["planner", "code_react"], - "chat_turn": 2, + "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, - "clear_structure": "True", - "brainstorming": "False", - "gui_design": "True", - "git_management": "False", - "self_improve": "False" + "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": "" }, } diff --git a/dev_opsgpt/connector/configs/phase_config.py b/dev_opsgpt/connector/configs/phase_config.py index 30b9de3..b8bf29d 100644 --- a/dev_opsgpt/connector/configs/phase_config.py +++ b/dev_opsgpt/connector/configs/phase_config.py @@ -55,9 +55,10 @@ PHASE_CONFIGS = { "do_using_tool": True }, "codeReactPhase": { - "phase_name": "codeReacttPhase", + "phase_name": "codeReactPhase", "phase_type": "BasePhase", - "chains": ["codeReactChain"], + # "chains": ["codePlannerChain", "codeReactChain"], + "chains": ["planChain", "codeReactChain"], "do_summary": False, "do_search": False, "do_doc_retrieval": False, @@ -65,15 +66,37 @@ PHASE_CONFIGS = { "do_tool_retrieval": False, "do_using_tool": False }, - "dataReactPhase": { - "phase_name": "dataReactPhase", + "codeToolReactPhase": { + "phase_name": "codeToolReactPhase", "phase_type": "BasePhase", - "chains": ["dataAnalystChain"], - "do_summary": True, + "chains": ["codeToolPlanChain", "codeToolReactChain"], + "do_summary": False, + "do_search": False, + "do_doc_retrieval": False, + "do_code_retrieval": False, + "do_tool_retrieval": False, + "do_using_tool": True + }, + "baseTaskPhase": { + "phase_name": "baseTaskPhase", + "phase_type": "BasePhase", + "chains": ["planChain", "executorChain"], + "do_summary": False, "do_search": False, "do_doc_retrieval": False, "do_code_retrieval": False, "do_tool_retrieval": False, "do_using_tool": False - } + }, + "metagpt_code_devlop": { + "phase_name": "metagpt_code_devlop", + "phase_type": "BasePhase", + "chains": ["metagptChain",], + "do_summary": False, + "do_search": False, + "do_doc_retrieval": False, + "do_code_retrieval": False, + "do_tool_retrieval": False, + "do_using_tool": False + }, } diff --git a/dev_opsgpt/connector/configs/prompts/__init__.py b/dev_opsgpt/connector/configs/prompts/__init__.py new file mode 100644 index 0000000..929ab77 --- /dev/null +++ b/dev_opsgpt/connector/configs/prompts/__init__.py @@ -0,0 +1,38 @@ +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 + +from .qa_template_prompt import QA_PROMPT, CODE_QA_PROMPT, QA_TEMPLATE_PROMPT + +from .executor_template_prompt import EXECUTOR_TEMPLATE_PROMPT +from .refine_template_prompt import REFINE_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", + "QA_PROMPT", "CODE_QA_PROMPT", "QA_TEMPLATE_PROMPT", + "EXECUTOR_TEMPLATE_PROMPT", + "REFINE_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" +] \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/prompts/checker_template_prompt.py b/dev_opsgpt/connector/configs/prompts/checker_template_prompt.py new file mode 100644 index 0000000..102ada1 --- /dev/null +++ b/dev_opsgpt/connector/configs/prompts/checker_template_prompt.py @@ -0,0 +1,37 @@ + +CHECKER_TEMPLATE_PROMPT = """#### Checker Assistance Guidance + +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 +**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. + +**Action Status:** Set to '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. +""" + +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/dev_opsgpt/connector/configs/prompts/executor_template_prompt.py b/dev_opsgpt/connector/configs/prompts/executor_template_prompt.py new file mode 100644 index 0000000..82b2b8a --- /dev/null +++ b/dev_opsgpt/connector/configs/prompts/executor_template_prompt.py @@ -0,0 +1,33 @@ +EXECUTOR_TEMPLATE_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 'finished' or 'coding'. If it's 'finished', the next action is to provide the final answer to the original question. If it's 'coding', 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 'finished' + +**Action:** The final answer to the original input question + +""" \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/prompts/input_template_prompt.py b/dev_opsgpt/connector/configs/prompts/input_template_prompt.py new file mode 100644 index 0000000..97ef84f --- /dev/null +++ b/dev_opsgpt/connector/configs/prompts/input_template_prompt.py @@ -0,0 +1,40 @@ + + +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/dev_opsgpt/connector/configs/prompts/intention_template_prompt.py b/dev_opsgpt/connector/configs/prompts/intention_template_prompt.py new file mode 100644 index 0000000..5f641ac --- /dev/null +++ b/dev_opsgpt/connector/configs/prompts/intention_template_prompt.py @@ -0,0 +1,14 @@ +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/dev_opsgpt/connector/configs/prompts/metagpt_prompt.py b/dev_opsgpt/connector/configs/prompts/metagpt_prompt.py new file mode 100644 index 0000000..39b92c4 --- /dev/null +++ b/dev_opsgpt/connector/configs/prompts/metagpt_prompt.py @@ -0,0 +1,218 @@ +PRD_WRITER_METAGPT_PROMPT = """#### PRD Writer Assistance Guidance + +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 = """#### PRD Writer Assistance Guidance + +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 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 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 = """#### Task Plan Assistance Guidance + +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 = """#### Code Writer Assistance Guidance + +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/dev_opsgpt/connector/configs/prompts/planner_template_prompt.py b/dev_opsgpt/connector/configs/prompts/planner_template_prompt.py new file mode 100644 index 0000000..fa9e1bb --- /dev/null +++ b/dev_opsgpt/connector/configs/prompts/planner_template_prompt.py @@ -0,0 +1,114 @@ + + +PLANNER_TEMPLATE_PROMPT = """#### Planner Assistance Guidance + +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 = """#### Tool Planner Assistance Guidance + +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. +You can use the following tool: {formatted_tools} + +#### 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/dev_opsgpt/connector/configs/prompts/qa_template_prompt.py b/dev_opsgpt/connector/configs/prompts/qa_template_prompt.py new file mode 100644 index 0000000..91e55ca --- /dev/null +++ b/dev_opsgpt/connector/configs/prompts/qa_template_prompt.py @@ -0,0 +1,52 @@ +QA_TEMPLATE_PROMPT = """#### Question Answer Assistance Guidance + +Based on the information provided, please answer the origin query concisely and professionally. +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. +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 +**Answer:** Response to the user's origin query based on Context and DocInfos. If DocInfos is empty, you can ignore it. +""" + + +CODE_QA_PROMPT = """#### Code Answer Assistance Guidance + +Based on the information provided, please answer the origin query concisely and professionally. +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. +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 +**Answer:** Response to the user's origin query based on DocInfos. If DocInfos is empty, you can ignore it. +""" + + +QA_PROMPT = """根据已知信息,简洁和专业的来回答问题。如果无法从中得到答案,请说 “根据已知信息无法回答该问题”,不允许在答案中添加编造成分,答案请使用中文。 +使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)。 +有效的 'action' 值为:'finished'(任务已经可以通过上下文信息可以回答) or 'continue' (上下文信息不足以回答问题)。 +在每个 $JSON_BLOB 中仅提供一个 action,如下所示: +``` +{{'action': $ACTION, 'content': '总结对话内容'}} +``` +按照以下格式进行回应: +问题:输入问题以回答 +行动:$ACTION +``` +$JSON_BLOB +``` +""" + +# CODE_QA_PROMPT = """【指令】根据已知信息来回答问""" \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/prompts/react_code_prompt.py b/dev_opsgpt/connector/configs/prompts/react_code_prompt.py new file mode 100644 index 0000000..596d747 --- /dev/null +++ b/dev_opsgpt/connector/configs/prompts/react_code_prompt.py @@ -0,0 +1,31 @@ + + +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 'finished' or 'coding'. If it's 'finished', the next action is to provide the final answer to the original question. If it's 'coding', the next step is to write the code. + +**Action:** Code according to your thoughts. (Please note that only the content printed out by the executed code can be observed in the subsequent observation.) Use this format for code: + +```python +# Write your code here +``` + +**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 'finished' + +**Action:** The final answer to the original input question + +""" \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/prompts/react_template_prompt.py b/dev_opsgpt/connector/configs/prompts/react_template_prompt.py new file mode 100644 index 0000000..6939f01 --- /dev/null +++ b/dev_opsgpt/connector/configs/prompts/react_template_prompt.py @@ -0,0 +1,31 @@ + + +REACT_TEMPLATE_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 'finished' or 'coding'. If it's 'finished', the next action is to provide the final answer to the original question. If it's 'coding', 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/Observation cycle as needed) + +**Thoughts:** I now know the final answer + +**Action Status:** Set to 'finished' + +**Action:** The final answer to the original input question + +""" \ No newline at end of file diff --git a/dev_opsgpt/connector/configs/prompts/react_tool_code_planner_prompt.py b/dev_opsgpt/connector/configs/prompts/react_tool_code_planner_prompt.py new file mode 100644 index 0000000..63af9b1 --- /dev/null +++ b/dev_opsgpt/connector/configs/prompts/react_tool_code_planner_prompt.py @@ -0,0 +1,49 @@ +REACT_TOOL_AND_CODE_PLANNER_PROMPT = """#### Tool and Code Sequence Breakdown Assistant +When users need assistance with deconstructing problems into a series of actionable plans using tools or code, your role is to provide a structured plan or a direct solution. +You may use the following tools: +{formatted_tools} +Depending on the user's query, the response will either be a plan detailing the use of tools and reasoning, or a direct answer if the problem does not require breaking down. + +#### Input Format + +**Origin Query:** user's query + +#### Follow this Response Format + +**Action Status:** Set to 'planning' to provide a sequence of tasks, or 'only_answer' to provide a direct response without a plan. + +**Action:** + +For planning: +```list +[ + "First step of the plan using a specified tool or a outline plan for code...", + "Next step in the plan...", + // Continue with additional steps as necessary +] +``` + +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/dev_opsgpt/connector/configs/prompts/react_tool_code_prompt.py b/dev_opsgpt/connector/configs/prompts/react_tool_code_prompt.py new file mode 100644 index 0000000..6e0efbb --- /dev/null +++ b/dev_opsgpt/connector/configs/prompts/react_tool_code_prompt.py @@ -0,0 +1,81 @@ +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 coding. 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 coding is required, outline the plan for executing this step. + +**Action Status:** finished, tool_using, or coding. (Choose one from these three statuses. If the task is done, set it to 'finished'. If using a tool, set it to 'tool_using'. If writing code, set it to 'coding'.) + +**Action:** + +If using a tool, output the following format to call the tool: + +```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:** finished + +**Action:** The final answer or guidance to the original input question. +""" + +# REACT_TOOL_AND_CODE_PROMPT = """你是一个使用工具与代码的助手。 +# 如果现有工具不足以完成整个任务,请不要添加不存在的工具,只使用现有工具完成可能的部分。 +# 如果当前步骤不能使用工具完成,将由代码来完成。 +# 有效的"action"值为:"finished"(已经完成用户的任务) 、 "tool_using" (使用工具来回答问题) 或 'coding'(结合总结下述思维链过程编写下一步的可执行代码)。 +# 尽可能地以有帮助和准确的方式回应人类,你可以使用以下工具: +# {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/dev_opsgpt/connector/configs/prompts/react_tool_prompt.py b/dev_opsgpt/connector/configs/prompts/react_tool_prompt.py new file mode 100644 index 0000000..416c36d --- /dev/null +++ b/dev_opsgpt/connector/configs/prompts/react_tool_prompt.py @@ -0,0 +1,81 @@ +REACT_TOOL_PROMPT = """#### Tool Agent Assistance Guidance + +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. + +#### Tool List + +you can use these tools:\n{formatted_tools} + +valid "tool_name" value is:\n{tool_names} + +#### Response Process + +**Question:** Start by understanding the input question to be answered. + +**Thoughts:** Based on the question and previous observations, plan the approach for using the tool effectively. + +**Action Status:** Set to either 'finished' or 'tool_using'. If 'finished', 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/Observation cycle as needed) + +**Thoughts:** Determine the final response based on the results. + +**Action Status:** Set to 'finished' + +**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"值为:"finished" 或 "tool_using" (使用工具来回答问题) +# 有效的"tool_name"值为:{tool_names} +# 请仅在每个$JSON_BLOB中提供一个action,如下所示: +# ``` +# {{{{ +# "action": $ACTION, +# "tool_name": $TOOL_NAME, +# "tool_params": $INPUT +# }}}} +# ``` + +# 按照以下格式进行回应: +# 问题:输入问题以回答 +# 思考:考虑之前和之后的步骤 +# 行动: +# ``` +# $JSON_BLOB +# ``` +# 观察:行动结果 +# ...(重复思考/行动/观察N次) +# 思考:我知道该如何回应 +# 行动: +# ``` +# {{{{ +# "action": "finished", +# "tool_name": "notool", +# "tool_params": "最终返回答案给到用户" +# }}}} +# ``` +# """ diff --git a/dev_opsgpt/connector/configs/prompts/refine_template_prompt.py b/dev_opsgpt/connector/configs/prompts/refine_template_prompt.py new file mode 100644 index 0000000..76cd466 --- /dev/null +++ b/dev_opsgpt/connector/configs/prompts/refine_template_prompt.py @@ -0,0 +1,30 @@ +REFINE_TEMPLATE_PROMPT = """#### Refiner Assistance Guidance + +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/dev_opsgpt/connector/configs/prompts/summary_template_prompt.py b/dev_opsgpt/connector/configs/prompts/summary_template_prompt.py new file mode 100644 index 0000000..0659cde --- /dev/null +++ b/dev_opsgpt/connector/configs/prompts/summary_template_prompt.py @@ -0,0 +1,20 @@ +CONV_SUMMARY_PROMPT = """尽可能地以有帮助和准确的方式回应人类,根据“背景信息”中的有效信息回答问题, +使用 JSON Blob 来指定一个返回的内容,提供一个 action(行动)。 +有效的 'action' 值为:'finished'(任务已经可以通过上下文信息可以回答) or 'continue' (根据背景信息回答问题)。 +在每个 $JSON_BLOB 中仅提供一个 action,如下所示: +``` +{{'action': $ACTION, 'content': '根据背景信息回答问题'}} +``` +按照以下格式进行回应: +问题:输入问题以回答 +行动: +``` +$JSON_BLOB +``` +""" + +CONV_SUMMARY_PROMPT = """尽可能地以有帮助和准确的方式回应人类 +根据“背景信息”中的有效信息回答问题,同时展现解答的过程和内容 +若能根“背景信息”回答问题,则直接回答 +否则,总结“背景信息”的内容 +""" \ No newline at end of file diff --git a/dev_opsgpt/connector/message_process.py b/dev_opsgpt/connector/message_process.py new file mode 100644 index 0000000..694d570 --- /dev/null +++ b/dev_opsgpt/connector/message_process.py @@ -0,0 +1,364 @@ +import re, traceback, uuid, copy, json, os +from loguru import logger + + +from configs.server_config import SANDBOX_SERVER +from configs.model_config import JUPYTER_WORK_PATH +from dev_opsgpt.connector.schema import ( + Memory, Task, Env, Role, Message, ActionStatus, CodeDoc, Doc +) +from dev_opsgpt.tools import DDGSTool, DocRetrieval, CodeRetrieval +from dev_opsgpt.sandbox import PyCodeBox, CodeBoxResponse + + +class MessageUtils: + def __init__(self, role: Role = None) -> None: + self.role = role + self.codebox = PyCodeBox( + remote_url=SANDBOX_SERVER["url"], + remote_ip=SANDBOX_SERVER["host"], + remote_port=SANDBOX_SERVER["port"], + token="mytoken", + do_code_exe=True, + do_remote=SANDBOX_SERVER["do_remote"], + do_check_net=False + ) + + def filter(self, message: Message, stop=None) -> Message: + + tool_params = self.parser_spec_key(message.role_content, "tool_params") + code_content = self.parser_spec_key(message.role_content, "code_content") + plan = self.parser_spec_key(message.role_content, "plan") + plans = self.parser_spec_key(message.role_content, "plans", do_search=False) + content = self.parser_spec_key(message.role_content, "content", do_search=False) + + # logger.debug(f"tool_params: {tool_params}, code_content: {code_content}, plan: {plan}, plans: {plans}, content: {content}") + role_content = tool_params or code_content or plan or plans or content + message.role_content = role_content or message.role_content + return message + + def inherit_extrainfo(self, input_message: Message, output_message: Message): + 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 + 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: + input_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 knowledge_basename: + docs = DocRetrieval.run(query, knowledge_basename, top_k, score_threshold) + message.db_docs = [Doc(**doc) for doc in docs] + return message + + def get_code_retrieval(self, message: Message) -> Message: + # DocRetrieval.run("langchain是什么", "DSADSAD") + query = message.input_query + code_engine_name = message.code_engine_name + history_node_list = message.history_node_list + 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) + message.code_docs = [CodeDoc(**doc) for doc in code_docs] + return message + + def get_tool_retrieval(self, message: Message) -> Message: + return message + + def step_router(self, message: Message) -> tuple[Message, ...]: + '''''' + # message = self.parser(message) + # logger.debug(f"message.action_status: {message.action_status}") + observation_message = None + if message.action_status == ActionStatus.CODING: + 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) + + 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" \ + if code_answer.code_exe_type == "error" else f"The return information after executing the above code is {code_answer.code_exe_response}" + + observation_message = Message( + role_name="observation", + role_type="func", #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.step_contents += [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."} + 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.step_contents += [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": code_prompt} + # logger.info(f"**Observation:** {message.action_status}, {message.observation}") + return message, observation_message + + def tool_step(self, message: Message) -> Message: + '''execute tool''' + # logger.debug(f"{message}") + observation_message = Message( + 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, + ) + # logger.debug(f"message: {message.action_status}, {message.tool_name}, {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" + message.step_contents += [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"} + for tool in message.tools: + if tool.name == message.tool_name: + tool_res = tool.func(**message.tool_params.get("tool_params", {})) + logger.debug(f"tool_res {tool_res}") + 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" + message.step_contents += [f"\n**Observation:** {tool_res}\n"] + observation_message.role_content = f"\n**Observation:** {tool_res}\n" + observation_message.parsed_output = {"Observation": tool_res} + break + + # logger.info(f"**Observation:** {message.action_status}, {message.observation}") + return message, observation_message + + def parser(self, message: Message) -> Message: + '''''' + content = message.role_content + parser_keys = ["action", "code_content", "code_filename", "tool_params", "plans"] + try: + s_json = self._parse_json(content) + message.action_status = s_json.get("action") + message.code_content = s_json.get("code_content") + message.tool_params = s_json.get("tool_params") + message.tool_name = s_json.get("tool_name") + message.code_filename = s_json.get("code_filename") + message.plans = s_json.get("plans") + # for parser_key in parser_keys: + # message.action_status = content.get(parser_key) + except Exception as e: + # logger.warning(f"{traceback.format_exc()}") + 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(.*?)```' + + # 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]: + 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 extract_content_from_backticks(text): + code_blocks = [] + lines = text.split('\n') + is_code_block = False + code_block = '' + language = '' + for line in lines: + if line.startswith('```') and not is_code_block: + is_code_block = True + language = line[3:] + code_block = '' + elif line.startswith('```') and is_code_block: + is_code_block = False + code_blocks.append({language.strip(): code_block.strip()}) + elif is_code_block: + code_block += line + '\n' + return code_blocks + + def parse_dict_to_dict(parsed_dict) -> dict: + code_pattern = r'```python\n(.*?)```' + tool_pattern = r'```json\n(.*?)```' + + pattern_dict = {"code": code_pattern, "json": tool_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): 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()) + except: + spec_parsed_dict[key] = _match.group(1).strip() + break + return spec_parsed_dict + + parsed_dict = parse_text_to_dict(content) + spec_parsed_dict = parse_dict_to_dict(parsed_dict) + action_value = parsed_dict.get('Action Status') + if action_value: + action_value = action_value.lower() + logger.info(f'{message.role_name}: action_value: {action_value}') + # action_value = self._match(r"'action':\s*'([^']*)'", content) if "'action'" in content else self._match(r'"action":\s*"([^"]*)"', content) + + code_content_value = spec_parsed_dict.get('code') + # code_content_value = self._match(r"'code_content':\s*'([^']*)'", content) if "'code_content'" in content else self._match(r'"code_content":\s*"([^"]*)"', content) + filename_value = self._match(r"'code_filename':\s*'([^']*)'", content) if "'code_filename'" in content else self._match(r'"code_filename":\s*"([^"]*)"', content) + + if action_value == 'tool_using': + tool_params_value = spec_parsed_dict.get('json') + else: + tool_params_value = None + # tool_params_value = spec_parsed_dict.get('tool_params') + # tool_params_value = self._match(r"'tool_params':\s*(\{[^{}]*\})", content, do_json=True) if "'tool_params'" in content \ + # else self._match(r'"tool_params":\s*(\{[^{}]*\})', content, do_json=True) + tool_name_value = self._match(r"'tool_name':\s*'([^']*)'", content) if "'tool_name'" in content else self._match(r'"tool_name":\s*"([^"]*)"', content) + plans_value = self._match(r"'plans':\s*(\[.*?\])", content, do_search=False) if "'plans'" in content else self._match(r'"plans":\s*(\[.*?\])', content, do_search=False, ) + # re解析 + message.action_status = action_value or "default" + message.code_content = code_content_value + message.code_filename = filename_value + message.tool_params = tool_params_value + message.tool_name = tool_name_value + message.plans = plans_value + message.parsed_output = parsed_dict + message.spec_parsed_output = spec_parsed_dict + + # logger.debug(f"确认当前的action: {message.action_status}") + + return message + + def parser_spec_key(self, content, key, do_search=True, do_json=False) -> str: + '''''' + key2pattern = { + "'action'": r"'action':\s*'([^']*)'", '"action"': r'"action":\s*"([^"]*)"', + "'code_content'": r"'code_content':\s*'([^']*)'", '"code_content"': r'"code_content":\s*"([^"]*)"', + "'code_filename'": r"'code_filename':\s*'([^']*)'", '"code_filename"': r'"code_filename":\s*"([^"]*)"', + "'tool_params'": r"'tool_params':\s*(\{[^{}]*\})", '"tool_params"': r'"tool_params":\s*(\{[^{}]*\})', + "'tool_name'": r"'tool_name':\s*'([^']*)'", '"tool_name"': r'"tool_name":\s*"([^"]*)"', + "'plans'": r"'plans':\s*(\[.*?\])", '"plans"': r'"plans":\s*(\[.*?\])', + "'content'": r"'content':\s*'([^']*)'", '"content"': r'"content":\s*"([^"]*)"', + } + + s_json = self._parse_json(content) + try: + if s_json and key in s_json: + return str(s_json[key]) + except: + pass + + keystr = f"'{key}'" if f"'{key}'" in content else f'"{key}"' + return self._match(key2pattern.get(keystr, fr"'{key}':\s*'([^']*)'"), content, do_search=do_search, do_json=do_json) + + def _match(self, pattern, s, do_search=True, do_json=False): + try: + if do_search: + match = re.search(pattern, s) + if match: + value = match.group(1).replace("\\n", "\n") + if do_json: + value = json.loads(value) + else: + value = None + else: + match = re.findall(pattern, s, re.DOTALL) + if match: + value = match[0] + if do_json: + value = json.loads(value) + else: + value = None + except Exception as e: + logger.warning(f"{traceback.format_exc()}") + + # logger.debug(f"pattern: {pattern}, s: {s}, match: {match}") + return value + + def _parse_json(self, s): + try: + pattern = r"```([^`]+)```" + match = re.findall(pattern, s) + if match: + return eval(match[0]) + except: + pass + return None + + + def save_code2file(self, message: Message, project_dir=JUPYTER_WORK_PATH): + 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/dev_opsgpt/connector/phase/base_phase.py b/dev_opsgpt/connector/phase/base_phase.py index cea23f4..c979778 100644 --- a/dev_opsgpt/connector/phase/base_phase.py +++ b/dev_opsgpt/connector/phase/base_phase.py @@ -8,14 +8,13 @@ from loguru import logger from dev_opsgpt.connector.agents import BaseAgent from dev_opsgpt.connector.chains import BaseChain from dev_opsgpt.tools.base_tool import BaseTools, Tool -from dev_opsgpt.connector.shcema.memory import Memory -from dev_opsgpt.connector.connector_schema import ( - Task, Env, Role, Message, Doc, Docs, AgentConfig, ChainConfig, PhaseConfig, CodeDoc, + +from dev_opsgpt.connector.schema import ( + Memory, Task, Env, Role, Message, Doc, Docs, AgentConfig, ChainConfig, PhaseConfig, CodeDoc, load_chain_configs, load_phase_configs, load_role_configs ) from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS -from dev_opsgpt.tools import DDGSTool, DocRetrieval, CodeRetrieval - +from dev_opsgpt.connector.message_process import MessageUtils role_configs = load_role_configs(AGETN_CONFIGS) chain_configs = load_chain_configs(CHAIN_CONFIGS) @@ -56,57 +55,71 @@ class BasePhase: chain_config = chain_config, role_config = role_config, ) + + self.message_utils = MessageUtils() self.phase_name = phase_name self.do_summary = do_summary self.do_search = do_search self.do_code_retrieval = do_code_retrieval self.do_doc_retrieval = do_doc_retrieval self.do_tool_retrieval = do_tool_retrieval - - self.global_message = Memory([]) + # + self.global_memory = Memory(messages=[]) # self.chain_message = Memory([]) self.phase_memory: List[Memory] = [] + # memory_pool dont have specific order + self.memory_pool = Memory(messages=[]) - def step(self, query: Message, history: Memory = None) -> Tuple[Message, Memory]: + def astep(self, query: Message, history: Memory = None) -> Tuple[Message, Memory]: summary_message = None - chain_message = Memory([]) - local_memory = Memory([]) + chain_message = Memory(messages=[]) + local_phase_memory = Memory(messages=[]) # do_search、do_doc_search、do_code_search - query = self.get_extrainfo_step(query) + query = self.message_utils.get_extrainfo_step(query, self.do_search, self.do_doc_retrieval, self.do_code_retrieval, self.do_tool_retrieval) input_message = copy.deepcopy(query) - self.global_message.append(input_message) + 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 - output_message, chain_memory = chain.step(input_message, history, background=chain_message) - output_message = self.inherit_extrainfo(input_message, output_message) + for output_message, local_chain_memory in chain.astep(input_message, history, background=chain_message, memory_pool=self.memory_pool): + # logger.debug(f"local_memory: {local_memory + 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) - self.global_message.append(output_message) - local_memory.extend(chain_memory) - - # whether use summary_llm + # whether to use summary_llm if self.do_summary: - logger.info(f"{self.conv_summary_agent.role.role_name} input global memory: {self.global_message.to_str_messages(content_key='step_content')}") - logger.info(f"{self.conv_summary_agent.role.role_name} input global memory: {self.global_message.to_str_messages(content_key='role_content')}") - summary_message = self.conv_summary_agent.run(query, background=self.global_message) + 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.arun(query, background=local_phase_memory, memory_pool=self.memory_pool): + pass + # summary_message = Message(**summary_message) summary_message.role_name = chain.chainConfig.chain_name - summary_message = self.conv_summary_agent.parser(summary_message) - summary_message = self.conv_summary_agent.filter(summary_message) - summary_message = self.inherit_extrainfo(output_message, summary_message) + summary_message = self.conv_summary_agent.message_utils.parser(summary_message) + summary_message = self.conv_summary_agent.message_utils.filter(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 - # message.db_docs = query.db_docs - # message.code_docs = query.code_docs - # message.search_docs = query.search_docs - return summary_message or output_message, local_memory + yield message, local_phase_memory + + def step(self, query: Message, history: Memory = None) -> Tuple[Message, Memory]: + for message, local_phase_memory in self.astep(query, history=history): + pass + return message, local_phase_memory def init_chains(self, phase_name, phase_config, chain_config, role_config, task=None, memory=None) -> List[BaseChain]: @@ -118,25 +131,33 @@ class BasePhase: chains = [] self.chain_module = importlib.import_module("dev_opsgpt.connector.chains") self.agent_module = importlib.import_module("dev_opsgpt.connector.agents") - phase = phase_configs.get(phase_name) - for chain_name in phase.chains: - logger.info(f"chain_name: {chain_name}") - # chain_class = getattr(self.chain_module, chain_name) - logger.debug(f"{chain_configs.keys()}") - chain_config = chain_configs[chain_name] - agents = [ - getattr(self.agent_module, role_configs[agent_name].role.agent_type)( - role_configs[agent_name].role, + phase = phase_configs.get(phase_name) + 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 = 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 = role_configs[agent_name] + baseAgent: BaseAgent = getattr(self.agent_module, agent_config.role.agent_type) + base_agent = baseAgent( + agent_config.role, task = task, memory = memory, - chat_turn=role_configs[agent_name].chat_turn, - do_search = role_configs[agent_name].do_search, - do_doc_retrieval = role_configs[agent_name].do_doc_retrieval, - do_tool_retrieval = role_configs[agent_name].do_tool_retrieval, + chat_turn=agent_config.chat_turn, + do_search = agent_config.do_search, + do_doc_retrieval = agent_config.do_doc_retrieval, + do_tool_retrieval = agent_config.do_tool_retrieval, + stop= agent_config.stop, + focus_agents=agent_config.focus_agents, + focus_message_keys=agent_config.focus_message_keys, ) - for agent_name in chain_config.agents - ] + agents.append(base_agent) + chain_instance = BaseChain( chain_config, agents, chain_config.chat_turn, do_checker=chain_configs[chain_name].do_checker, @@ -145,56 +166,57 @@ class BasePhase: return chains - def get_extrainfo_step(self, input_message): - if self.do_doc_retrieval: - input_message = self.get_doc_retrieval(input_message) + # def get_extrainfo_step(self, input_message): + # if self.do_doc_retrieval: + # input_message = self.get_doc_retrieval(input_message) - logger.debug(F"self.do_code_retrieval: {self.do_code_retrieval}") - if self.do_code_retrieval: - input_message = self.get_code_retrieval(input_message) + # # logger.debug(F"self.do_code_retrieval: {self.do_code_retrieval}") + # if self.do_code_retrieval: + # input_message = self.get_code_retrieval(input_message) - if self.do_search: - input_message = self.get_search_retrieval(input_message) + # if self.do_search: + # input_message = self.get_search_retrieval(input_message) - return input_message + # return input_message - def inherit_extrainfo(self, input_message: Message, output_message: Message): - 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) - return output_message + # def inherit_extrainfo(self, input_message: Message, output_message: Message): + # 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 + # return output_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_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 knowledge_basename: - docs = DocRetrieval.run(query, knowledge_basename, top_k, score_threshold) - message.db_docs = [Doc(**doc) for doc in 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 knowledge_basename: + # docs = DocRetrieval.run(query, knowledge_basename, top_k, score_threshold) + # message.db_docs = [Doc(**doc) for doc in docs] + # return message - def get_code_retrieval(self, message: Message) -> Message: - # DocRetrieval.run("langchain是什么", "DSADSAD") - query = message.input_query - code_engine_name = message.code_engine_name - history_node_list = message.history_node_list - code_docs = CodeRetrieval.run(code_engine_name, query, code_limit=message.top_k, history_node_list=history_node_list) - message.code_docs = [CodeDoc(**doc) for doc in code_docs] - return message + # def get_code_retrieval(self, message: Message) -> Message: + # # DocRetrieval.run("langchain是什么", "DSADSAD") + # query = message.input_query + # code_engine_name = message.code_engine_name + # history_node_list = message.history_node_list + # code_docs = CodeRetrieval.run(code_engine_name, query, code_limit=message.top_k, history_node_list=history_node_list) + # message.code_docs = [CodeDoc(**doc) for doc in code_docs] + # return message - def get_tool_retrieval(self, message: Message) -> Message: - return message + # def get_tool_retrieval(self, message: Message) -> Message: + # return message def update(self) -> Memory: pass @@ -205,7 +227,7 @@ class BasePhase: ) def get_memory_str(self, do_all_memory=True, content_key="role_content") -> str: - memory = self.global_message if do_all_memory else self.phase_memory + 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]: diff --git a/dev_opsgpt/connector/schema/__init__.py b/dev_opsgpt/connector/schema/__init__.py new file mode 100644 index 0000000..f858055 --- /dev/null +++ b/dev_opsgpt/connector/schema/__init__.py @@ -0,0 +1,9 @@ +from .memory import Memory +from .general_schema import * +from .message import Message + +__all__ = [ + "Memory", "ActionStatus", "Doc", "CodeDoc", "Task", + "Env", "Role", "ChainConfig", "AgentConfig", "PhaseConfig", "Message", + "load_role_configs", "load_chain_configs", "load_phase_configs" +] \ No newline at end of file diff --git a/dev_opsgpt/connector/connector_schema.py b/dev_opsgpt/connector/schema/general_schema.py similarity index 58% rename from dev_opsgpt/connector/connector_schema.py rename to dev_opsgpt/connector/schema/general_schema.py index 75c5387..2281762 100644 --- a/dev_opsgpt/connector/connector_schema.py +++ b/dev_opsgpt/connector/schema/general_schema.py @@ -16,11 +16,50 @@ class ActionStatus(Enum): EXECUTING_CODE = "executing_code" EXECUTING_TOOL = "executing_tool" DEFAUILT = "default" + CODING2FILE = "coding2file" def __eq__(self, other): if isinstance(other, str): return self.value == other return super().__eq__(other) + + +class RoleTypeEnums(Enum): + SYSTEM = "system" + USER = "user" + ASSISTANT = "assistant" + FUNCTION = "function" + OBSERVATION = "observation" + + def __eq__(self, other): + if isinstance(other, str): + return self.value == other + return super().__eq__(other) + + +class InputKeyEnums(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_ONE_MEMORY = "self_one_memory" + # chain memory + CHAIN_ONE_MEMORY = "chain_one_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 @@ -117,10 +156,13 @@ class ChainConfig(BaseModel): class AgentConfig(BaseModel): role: Role + stop: str = None chat_turn: int = 1 do_search: bool = False do_doc_retrieval: bool = False do_tool_retrieval: bool = False + focus_agents: List = [] + focus_message_keys: List = [] class PhaseConfig(BaseModel): @@ -133,92 +175,6 @@ class PhaseConfig(BaseModel): do_code_retrieval: bool = False do_tool_retrieval: bool = False -class Message(BaseModel): - role_name: str - role_type: str - role_prompt: str = None - input_query: str = None - - # 模型最终返回 - role_content: str = None - role_contents: List[str] = [] - step_content: str = None - step_contents: List[str] = [] - chain_content: str = None - chain_contents: List[str] = [] - - # 模型结果解析 - plans: List[str] = None - code_content: str = None - code_filename: str = None - tool_params: str = None - tool_name: str = None - - # 执行结果 - action_status: str = ActionStatus.DEFAUILT - code_answer: str = None - tool_answer: str = None - observation: str = None - figures: Dict[str, str] = {} - - # 辅助信息 - tools: List[BaseTool] = [] - task: Task = None - db_docs: List['Doc'] = [] - code_docs: List['CodeDoc'] = [] - search_docs: List['Doc'] = [] - - # 执行输入 - phase_name: str = None - chain_name: str = None - do_search: bool = False - doc_engine_name: str = None - code_engine_name: str = None - search_engine_name: str = None - top_k: int = 3 - 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] = [] - - - def to_tuple_message(self, return_all: bool = False, content_key="role_content"): - if content_key == "role_content": - role_content = self.role_content - elif content_key == "step_content": - role_content = self.step_content or self.role_content - else: - role_content =self.role_content - - if return_all: - return (self.role_name, self.role_type, role_content) - else: - return (self.role_name, role_content) - return (self.role_type, re.sub("}", "}}", re.sub("{", "{{", str(self.role_content)))) - - def to_dict_message(self, return_all: bool = False, content_key="role_content"): - if content_key == "role_content": - role_content =self.role_content - elif content_key == "step_content": - role_content = self.step_content or self.role_content - else: - role_content =self.role_content - - if return_all: - return vars(self) - else: - return {"role": self.role_name, "content": role_content} - - def is_system_role(self,): - return self.role_type == "system" - - 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()]) - - def load_role_configs(config) -> Dict[str, AgentConfig]: if isinstance(config, str): diff --git a/dev_opsgpt/connector/shcema/memory.py b/dev_opsgpt/connector/schema/memory.py similarity index 51% rename from dev_opsgpt/connector/shcema/memory.py rename to dev_opsgpt/connector/schema/memory.py index f5e6a5f..70d2809 100644 --- a/dev_opsgpt/connector/shcema/memory.py +++ b/dev_opsgpt/connector/schema/memory.py @@ -1,17 +1,18 @@ from pydantic import BaseModel -from typing import List +from typing import List, Union from loguru import logger -from dev_opsgpt.connector.connector_schema import Message +from .message import Message from dev_opsgpt.utils.common_utils import ( save_to_jsonl_file, save_to_json_file, read_json_file, read_jsonl_file ) -class Memory: - - def __init__(self, messages: List[Message] = []): - self.messages = messages +class Memory(BaseModel): + messages: List[Message] = [] + + # def __init__(self, messages: List[Message] = []): + # self.messages = messages def append(self, message: Message): self.messages.append(message) @@ -28,8 +29,9 @@ class Memory: def delete(self, ): pass - def get_messages(self, ) -> List[Message]: - return self.messages + def get_messages(self, k=0) -> List[Message]: + """Return the most recent k memories, return all when k=0""" + return self.messages[-k:] def save(self, file_type="jsonl", return_all=True): try: @@ -57,32 +59,56 @@ class Memory: return False - def to_tuple_messages(self, return_system: bool = False, return_all: bool = False, content_key="role_content"): + def to_tuple_messages(self, return_all: bool = True, content_key="role_content", filter_roles=[]): # 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 not message.is_system_role() | return_system + if message.role_name not in filter_roles ] - def to_dict_messages(self, return_system: bool = False, return_all: bool = False, content_key="role_content"): + def to_dict_messages(self, return_all: bool = True, content_key="role_content", filter_roles=[]): return [ message.to_dict_message(return_all, content_key) for message in self.messages - if not message.is_system_role() | return_system + if message.role_name not in filter_roles ] - def to_str_messages(self, return_system: bool = False, return_all: bool = False, content_key="role_content"): + def to_str_messages(self, return_all: bool = True, content_key="role_content", filter_roles=[]): + # for message in self.messages: + # logger.debug(f"{message.to_tuple_message(return_all, content_key)}") # logger.debug(f"{[message.to_tuple_message(return_all, content_key) for message in self.messages ]}") - return "\n".join([ - ": ".join(message.to_tuple_message(return_all, content_key)) for message in self.messages - if not message.is_system_role() | return_system + return "\n\n".join([message.to_str_content(return_all, content_key) 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:]] + + 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([message for memory in memorys for message in memory.get_messages()]) + 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()]) \ No newline at end of file + 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/dev_opsgpt/connector/schema/message.py b/dev_opsgpt/connector/schema/message.py new file mode 100644 index 0000000..862d7a3 --- /dev/null +++ b/dev_opsgpt/connector/schema/message.py @@ -0,0 +1,98 @@ +from pydantic import BaseModel +from loguru import logger + +from .general_schema import * + + +class Message(BaseModel): + chat_index: str = None + role_name: str + role_type: str + role_prompt: str = None + input_query: str = None + origin_query: str = None + + # 模型最终返回 + role_content: str = None + role_contents: List[str] = [] + step_content: str = None + step_contents: List[str] = [] + chain_content: str = None + chain_contents: List[str] = [] + + # 模型结果解析 + 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] = [] + + # 执行结果 + action_status: str = ActionStatus.DEFAUILT + agent_index: int = None + code_answer: str = None + tool_answer: str = None + observation: str = None + figures: Dict[str, str] = {} + + # 辅助信息 + tools: List[BaseTool] = [] + task: Task = None + db_docs: List['Doc'] = [] + code_docs: List['CodeDoc'] = [] + search_docs: List['Doc'] = [] + agents: List = [] + + # 执行输入 + 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 + 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] = [] + + 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_all: bool = True, content_key="role_content"): + role_content = self.to_str_content(False, content_key) + if return_all: + return {"role": self.role_name, "content": role_content} + else: + return vars(self) + + def to_str_content(self, return_all: bool = True, content_key="role_content"): + 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 + else: + role_content = self.role_content or self.input_query + + if return_all: + return f"{self.role_name}: {role_content}" + else: + return role_content + + def is_system_role(self,): + return self.role_type == "system" + + 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/dev_opsgpt/connector/shcema/__init__.py b/dev_opsgpt/connector/shcema/__init__.py deleted file mode 100644 index d523fc9..0000000 --- a/dev_opsgpt/connector/shcema/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .memory import Memory - - -__all__ = [ - "Memory" -] \ No newline at end of file diff --git a/dev_opsgpt/connector/utils.py b/dev_opsgpt/connector/utils.py index 9684505..e6a95de 100644 --- a/dev_opsgpt/connector/utils.py +++ b/dev_opsgpt/connector/utils.py @@ -1,5 +1,30 @@ +import re + +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 prompt_cost(model_type: str, num_prompt_tokens: float, num_completion_tokens: float): input_cost_map = { "gpt-3.5-turbo": 0.0015, diff --git a/dev_opsgpt/db_handler/__init__.py b/dev_opsgpt/db_handler/__init__.py new file mode 100644 index 0000000..83c9d92 --- /dev/null +++ b/dev_opsgpt/db_handler/__init__.py @@ -0,0 +1,7 @@ +# encoding: utf-8 +''' +@author: 温进 +@file: __init__.py.py +@time: 2023/11/16 下午3:15 +@desc: +''' \ No newline at end of file diff --git a/dev_opsgpt/db_handler/graph_db_handler/__init__.py b/dev_opsgpt/db_handler/graph_db_handler/__init__.py new file mode 100644 index 0000000..6d3396a --- /dev/null +++ b/dev_opsgpt/db_handler/graph_db_handler/__init__.py @@ -0,0 +1,7 @@ +# encoding: utf-8 +''' +@author: 温进 +@file: __init__.py.py +@time: 2023/11/20 下午3:07 +@desc: +''' \ No newline at end of file diff --git a/dev_opsgpt/db_handler/graph_db_handler/nebula_handler.py b/dev_opsgpt/db_handler/graph_db_handler/nebula_handler.py new file mode 100644 index 0000000..8629b86 --- /dev/null +++ b/dev_opsgpt/db_handler/graph_db_handler/nebula_handler.py @@ -0,0 +1,263 @@ +# 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 = ''): + ''' + + @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 space_name: + cypher = f'USE {space_name};{cypher}' + logger.debug(cypher) + resp = session.execute(cypher) + + 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) + 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) + 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 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/dev_opsgpt/db_handler/vector_db_handler/__init__.py b/dev_opsgpt/db_handler/vector_db_handler/__init__.py new file mode 100644 index 0000000..2b67ecf --- /dev/null +++ b/dev_opsgpt/db_handler/vector_db_handler/__init__.py @@ -0,0 +1,7 @@ +# encoding: utf-8 +''' +@author: 温进 +@file: __init__.py.py +@time: 2023/11/20 下午3:08 +@desc: +''' \ No newline at end of file diff --git a/dev_opsgpt/db_handler/vector_db_handler/chroma_handler.py b/dev_opsgpt/db_handler/vector_db_handler/chroma_handler.py new file mode 100644 index 0000000..07822e1 --- /dev/null +++ b/dev_opsgpt/db_handler/vector_db_handler/chroma_handler.py @@ -0,0 +1,140 @@ +# 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 + ''' + self.client = chromadb.PersistentClient(path) + 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/dev_opsgpt/embeddings/get_embedding.py b/dev_opsgpt/embeddings/get_embedding.py new file mode 100644 index 0000000..b7b8cdc --- /dev/null +++ b/dev_opsgpt/embeddings/get_embedding.py @@ -0,0 +1,39 @@ +# 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 dev_opsgpt.embeddings.openai_embedding import OpenAIEmbedding +from dev_opsgpt.embeddings.huggingface_embedding import HFEmbedding + + +def get_embedding(engine: str, text_list: list): + ''' + get embedding + @param engine: openai / hf + @param text_list: + @return: + ''' + emb_res = {} + + if engine == 'openai': + oae = OpenAIEmbedding() + emb_res = oae.get_emb(text_list) + elif engine == 'model': + hfe = HFEmbedding(EMBEDDING_MODEL) + 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/dev_opsgpt/embeddings/huggingface_embedding.py b/dev_opsgpt/embeddings/huggingface_embedding.py new file mode 100644 index 0000000..1175d83 --- /dev/null +++ b/dev_opsgpt/embeddings/huggingface_embedding.py @@ -0,0 +1,49 @@ +# 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 dev_opsgpt.embeddings.utils import load_embeddings + + +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): + self.model = load_embeddings(model=model_name, device=EMBEDDING_DEVICE) + logger.debug('load success') + + def get_emb(self, text_list): + ''' + get embedding + @param text_list: + @return: + ''' + logger.info('st') + emb_res = self.model.embed_documents(text_list) + logger.info('ed') + 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/dev_opsgpt/embeddings/openai_embedding.py b/dev_opsgpt/embeddings/openai_embedding.py new file mode 100644 index 0000000..02c2d92 --- /dev/null +++ b/dev_opsgpt/embeddings/openai_embedding.py @@ -0,0 +1,50 @@ +# 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 + +from configs.model_config import OPENAI_API_BASE + + +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/dev_opsgpt/embeddings/utils.py b/dev_opsgpt/embeddings/utils.py index 5b19371..c762c04 100644 --- a/dev_opsgpt/embeddings/utils.py +++ b/dev_opsgpt/embeddings/utils.py @@ -9,4 +9,6 @@ def load_embeddings(model: str, device: str): logger.info("load embedding model: {}, {}".format(model, embedding_model_dict[model])) embeddings = HuggingFaceEmbeddings(model_name=embedding_model_dict[model], model_kwargs={'device': device}) - return embeddings \ No newline at end of file + return embeddings + + diff --git a/dev_opsgpt/orm/commands/code_base_cds.py b/dev_opsgpt/orm/commands/code_base_cds.py index 939d1d5..258a039 100644 --- a/dev_opsgpt/orm/commands/code_base_cds.py +++ b/dev_opsgpt/orm/commands/code_base_cds.py @@ -11,12 +11,14 @@ from dev_opsgpt.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): +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) + code_file_num=code_file_num, do_interpret=do_interpret) session.add(cb) else: cb.code_path = code_path @@ -47,10 +49,10 @@ def cb_exists(session, code_name): 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 = cb.code_name, cb.code_path, cb.code_graph_node_num + 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 - return code_name, code_path, code_graph_node_num + 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 @@ -71,7 +73,8 @@ def get_cb_detail(session, code_name: str) -> dict: "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 + 'code_file_num': cb.code_file_num, + 'do_interpret': cb.do_interpret } else: return { diff --git a/dev_opsgpt/orm/schemas/base_schema.py b/dev_opsgpt/orm/schemas/base_schema.py index 6c497d3..2657884 100644 --- a/dev_opsgpt/orm/schemas/base_schema.py +++ b/dev_opsgpt/orm/schemas/base_schema.py @@ -58,6 +58,7 @@ class CodeBaseSchema(Base): 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): @@ -65,5 +66,6 @@ class CodeBaseSchema(Base): code_name='{self.code_name}', code_path='{self.code_path}', code_graph_node_num='{self.code_graph_node_num}', - code_file_num='{self.code_file_num}' + code_file_num='{self.code_file_num}', + do_interpret='{self.do_interpret}', create_time='{self.create_time}')>""" diff --git a/dev_opsgpt/sandbox/pycodebox.py b/dev_opsgpt/sandbox/pycodebox.py index 2012752..5914ef2 100644 --- a/dev_opsgpt/sandbox/pycodebox.py +++ b/dev_opsgpt/sandbox/pycodebox.py @@ -92,11 +92,11 @@ class PyCodeBox(BaseBox): "metadata": {}, "content": { "code": code_text, - "silent": True, - "store_history": True, + "silent": False, # True,则内核会执行代码,但不会发送执行结果(如输出) + "store_history": True, # True,则执行的代码会被记录在交互式环境的历史记录中 "user_expressions": {}, - "allow_stdin": False, - "stop_on_error": True, + "allow_stdin": False, # True,允许代码执行时接受用户输入 + "stop_on_error": True, # True,当执行中遇到错误时,后续代码将不会继续执行。 }, "channel": "shell", "buffers": [], @@ -163,7 +163,7 @@ class PyCodeBox(BaseBox): return CodeBoxResponse( code_exe_type="text", code_text=code_text, - code_exe_response=result or "Code run successfully (no output),可能没有打印需要确认的变量", + code_exe_response=result or "Code run successfully (no output)", code_exe_status=200, do_code_exe=self.do_code_exe ) diff --git a/dev_opsgpt/service/cb_api.py b/dev_opsgpt/service/cb_api.py index 937f29d..f4e9ef0 100644 --- a/dev_opsgpt/service/cb_api.py +++ b/dev_opsgpt/service/cb_api.py @@ -23,7 +23,9 @@ from configs.model_config import ( CB_ROOT_PATH ) -from dev_opsgpt.codebase_handler.codebase_handler import CodeBaseHandler +# from dev_opsgpt.codebase_handler.codebase_handler import CodeBaseHandler + +from dev_opsgpt.codechat.codebase_handler.codebase_handler import CodeBaseHandler from loguru import logger @@ -33,10 +35,12 @@ async def list_cbs(): return ListResponse(data=list_cbs_from_db()) -async def create_cb(cb_name: str = Body(..., examples=["samples"]), - code_path: str = Body(..., examples=["samples"]) +async def create_cb(zip_file, + cb_name: str = Body(..., examples=["samples"]), + code_path: str = Body(..., examples=["samples"]), + do_interpret: bool = Body(..., examples=["samples"]) ) -> BaseResponse: - logger.info('cb_name={}, zip_path={}'.format(cb_name, code_path)) + logger.info('cb_name={}, zip_path={}, do_interpret={}'.format(cb_name, code_path, do_interpret)) # Create selected knowledge base if not validate_kb_name(cb_name): @@ -50,17 +54,16 @@ async def create_cb(cb_name: str = Body(..., examples=["samples"]), try: logger.info('start build code base') - cbh = CodeBaseHandler(cb_name, code_path, cb_root_path=CB_ROOT_PATH) - cbh.import_code(do_save=True) - code_graph_node_num = len(cbh.nh) - code_file_num = len(cbh.lcdh) + cbh = CodeBaseHandler(cb_name, code_path) + 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, code_graph_node_num, code_file_num) + 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}") @@ -81,6 +84,11 @@ async def delete_cb(cb_name: str = Body(..., examples=["samples"])) -> BaseRespo # delete local file shutil.rmtree(CB_ROOT_PATH + os.sep + cb_name) + + # delete from codebase + cbh = CodeBaseHandler(cb_name) + cbh.delete_codebase(codebase_name=cb_name) + except Exception as e: print(e) return BaseResponse(code=500, msg=f"删除代码知识库出错: {e}") @@ -91,24 +99,25 @@ async def delete_cb(cb_name: str = Body(..., examples=["samples"])) -> BaseRespo 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(...)) -> dict: 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)) try: # load codebase - cbh = CodeBaseHandler(code_name=cb_name, cb_root_path=CB_ROOT_PATH) - cbh.import_code(do_load=True) + cbh = CodeBaseHandler(codebase_name=cb_name) # search code - related_code, related_node = cbh.search_code(query, code_limit=code_limit, history_node_list=history_node_list) + context, related_vertices = cbh.search_code(query, search_type=search_type, limit=code_limit) res = { - 'related_code': related_code, - 'related_node': related_node + 'context': context, + 'related_vertices': related_vertices } return res except Exception as e: diff --git a/dev_opsgpt/service/sdfile_api.py b/dev_opsgpt/service/sdfile_api.py index 17336b5..2d92915 100644 --- a/dev_opsgpt/service/sdfile_api.py +++ b/dev_opsgpt/service/sdfile_api.py @@ -12,7 +12,7 @@ from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import StreamingResponse, FileResponse from fastapi import File, UploadFile -from dev_opsgpt.utils.server_utils import BaseResponse, ListResponse +from dev_opsgpt.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 @@ -35,7 +35,8 @@ async def sd_upload_file(file: UploadFile = File(...), work_dir: str = JUPYTER_W 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": FileResponse(os.path.join(work_dir, filename), filename=save_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): @@ -78,7 +79,7 @@ def create_app(): app.get("/sdfiles/download", tags=["files upload and download"], - response_model=BaseResponse, + response_model=DataResponse, summary="从沙盒下载文件" )(sd_download_file) diff --git a/dev_opsgpt/tools/__init__.py b/dev_opsgpt/tools/__init__.py index 130dc71..5412f03 100644 --- a/dev_opsgpt/tools/__init__.py +++ b/dev_opsgpt/tools/__init__.py @@ -1,3 +1,5 @@ +import importlib + from .base_tool import toLangchainTools, get_tool_schema, BaseToolModel from .weather import WeatherInfo, DistrictInfo from .multiplier import Multiplier @@ -7,27 +9,22 @@ 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 -TOOL_SETS = [ - "WeatherInfo", "WorldTimeGetTimezoneByArea", "Multiplier", "DistrictInfo", "KSigmaDetector", "MetricsQuery", "DDGSTool", - "DocRetrieval", "CodeRetrieval" - ] -TOOL_DICT = { - "WeatherInfo": WeatherInfo, - "WorldTimeGetTimezoneByArea": WorldTimeGetTimezoneByArea, - "Multiplier": Multiplier, - "DistrictInfo": DistrictInfo, - "KSigmaDetector": KSigmaDetector, - "MetricsQuery": MetricsQuery, - "DDGSTool": DDGSTool, - "DocRetrieval": DocRetrieval, - "CodeRetrieval": CodeRetrieval -} - -__all__ = [ - "WeatherInfo", "WorldTimeGetTimezoneByArea", "Multiplier", "DistrictInfo", "KSigmaDetector", "MetricsQuery", "DDGSTool", - "DocRetrieval", "CodeRetrieval", - "toLangchainTools", "get_tool_schema", "tool_sets", "BaseToolModel" +IMPORT_TOOL = [ + WeatherInfo, DistrictInfo, Multiplier, WorldTimeGetTimezoneByArea, + KSigmaDetector, MetricsQuery, DDGSTool, DocRetrieval, CodeRetrieval, + BaiduOcrTool, StockInfo, StockName ] +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/dev_opsgpt/tools/cb_query_tool.py b/dev_opsgpt/tools/cb_query_tool.py index c162252..4295829 100644 --- a/dev_opsgpt/tools/cb_query_tool.py +++ b/dev_opsgpt/tools/cb_query_tool.py @@ -35,13 +35,26 @@ class CodeRetrieval(BaseToolModel): code: str = Field(..., description="检索代码") @classmethod - def run(cls, code_base_name, query, code_limit=CODE_SEARCH_TOP_K, history_node_list=[]): + def run(cls, code_base_name, query, code_limit=CODE_SEARCH_TOP_K, history_node_list=[], search_type="tag"): """excute your tool!""" - codes = search_code(code_base_name, query, code_limit, history_node_list=history_node_list) - return_codes = [] - related_code = codes['related_code'] - related_nodes = codes['related_node'] + + 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) + 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}) - for idx, code in enumerate(related_code): - return_codes.append({'index': idx, 'code': code, "related_nodes": related_nodes}) return return_codes diff --git a/dev_opsgpt/tools/multiplier.py b/dev_opsgpt/tools/multiplier.py index e40d382..fee4971 100644 --- a/dev_opsgpt/tools/multiplier.py +++ b/dev_opsgpt/tools/multiplier.py @@ -32,7 +32,4 @@ class Multiplier(BaseToolModel): @staticmethod def run(a, b): - return a * b - -def multi_run(a, b): - return a * b \ No newline at end of file + return a * b \ No newline at end of file diff --git a/dev_opsgpt/tools/ocr_tool.py b/dev_opsgpt/tools/ocr_tool.py new file mode 100644 index 0000000..f19d1e6 --- /dev/null +++ b/dev_opsgpt/tools/ocr_tool.py @@ -0,0 +1,96 @@ +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=JUPYTER_WORK_PATH, API_KEY=os.environ.get("BAIDU_OCR_API_KEY"), SECRET_KEY=os.environ.get("BAIDU_OCR_SECRET_KEY")): + 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/dev_opsgpt/tools/stock_tool.py b/dev_opsgpt/tools/stock_tool.py new file mode 100644 index 0000000..2f4e946 --- /dev/null +++ b/dev_opsgpt/tools/stock_tool.py @@ -0,0 +1,189 @@ + +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 dev_opsgpt.utils.common_utils import read_json_file + + +cur_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +stock_infos = read_json_file(os.path.join(cur_dir, "../sources/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/dev_opsgpt/utils/common_utils.py b/dev_opsgpt/utils/common_utils.py index 64d2fa6..047c831 100644 --- a/dev_opsgpt/utils/common_utils.py +++ b/dev_opsgpt/utils/common_utils.py @@ -73,14 +73,14 @@ def save_to_json_file(data, filename): def file_normalize(file: Union[str, Path, bytes], filename=None): - logger.debug(f"{file}") + # 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) + # logger.debug(file) filename = filename or file.name return file, filename diff --git a/dev_opsgpt/utils/nebula_cp.sh b/dev_opsgpt/utils/nebula_cp.sh new file mode 100644 index 0000000..b98de0c --- /dev/null +++ b/dev_opsgpt/utils/nebula_cp.sh @@ -0,0 +1,3 @@ +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/dev_opsgpt/utils/postprocess.py b/dev_opsgpt/utils/postprocess.py index 9929aaa..fa65bc3 100644 --- a/dev_opsgpt/utils/postprocess.py +++ b/dev_opsgpt/utils/postprocess.py @@ -5,8 +5,8 @@ @time: 2023/11/9 下午4:01 @desc: ''' +import html def replace_lt_gt(text: str): - text = text.replace('<', '<') - text = text.replace('>', '>') + text = html.unescape(text) return text \ No newline at end of file diff --git a/dev_opsgpt/utils/server_utils.py b/dev_opsgpt/utils/server_utils.py index 27a133f..926590d 100644 --- a/dev_opsgpt/utils/server_utils.py +++ b/dev_opsgpt/utils/server_utils.py @@ -1,6 +1,6 @@ import pydantic from pydantic import BaseModel -from typing import List +from typing import List, Union import torch from fastapi import FastAPI from pathlib import Path @@ -21,6 +21,18 @@ class BaseResponse(BaseModel): } } +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") diff --git a/dev_opsgpt/webui/code.py b/dev_opsgpt/webui/code.py index 0e6a6c4..5b6bffd 100644 --- a/dev_opsgpt/webui/code.py +++ b/dev_opsgpt/webui/code.py @@ -84,6 +84,10 @@ def code_page(api: ApiRequest): accept_multiple_files=False, ) + do_interpret = st.checkbox('**代码解读**', value=True, help='代码解读会针对每个代码文件通过 LLM 获取解释并且向量化存储。当代码文件较多时,\ + 导入速度会变慢,且如果使用收费 API 的话可能会造成较大花费。如果要使用基于描述的代码问答模式,此项必须勾选', key='do_interpret') + + logger.info(f'do_interpret={do_interpret}') submit_create_kb = st.form_submit_button( "新建", use_container_width=True, @@ -104,6 +108,7 @@ def code_page(api: ApiRequest): ret = api.create_code_base( cb_name, file, + do_interpret, no_remote_api=True ) st.toast(ret.get("msg", " ")) @@ -124,6 +129,7 @@ def code_page(api: ApiRequest): cb_details.get('code_file_num', 'unknown'))) st.write('代码知识库 `{}` 知识图谱节点数=`{}`'.format(cb_details['code_name'], cb_details['code_graph_node_num'])) + st.write('代码知识库 `{}` 是否进行代码解读=`{}`'.format(cb_details['code_name'], cb_details['do_interpret'])) st.divider() diff --git a/dev_opsgpt/webui/dialogue.py b/dev_opsgpt/webui/dialogue.py index 03f6d93..95c2413 100644 --- a/dev_opsgpt/webui/dialogue.py +++ b/dev_opsgpt/webui/dialogue.py @@ -9,23 +9,34 @@ from dev_opsgpt.utils import * from dev_opsgpt.tools import TOOL_SETS from dev_opsgpt.chat.search_chat import SEARCH_ENGINES from dev_opsgpt.connector import PHASE_LIST, PHASE_CONFIGS +from dev_opsgpt.service.service_factory import get_cb_details_by_cb_name chat_box = ChatBox( assistant_avatar="../sources/imgs/devops-chatbot2.png" ) GLOBAL_EXE_CODE_TEXT = "" +GLOBAL_MESSAGE = {"figures": {}, "final_contents": {}} - -def get_messages_history(history_len: int) -> List[Dict]: +def get_messages_history(history_len: int, isDetailed=False) -> List[Dict]: def filter(msg): ''' 针对当前简单文本对话,只返回每条消息的第一个element的内容 ''' content = [x._content for x in msg["elements"] if x._output_method in ["markdown", "text"]] + content = content[0] if content else "" + if isDetailed: + for k, v in GLOBAL_MESSAGE["final_contents"].items(): + if k == content: + content = v[-1] + break + + for k, v in GLOBAL_MESSAGE["figures"].items(): + content = content.replace(v, k) + return { "role": msg["role"], - "content": content[0] if content else "", + "content": content, } history = chat_box.filter_history(100000, filter) # workaround before upgrading streamlit-chatbox. @@ -39,6 +50,18 @@ def get_messages_history(history_len: int) -> List[Dict]: return history[-i:] +def upload2sandbox(upload_file, api: ApiRequest): + if upload_file is None: + res = {"msg": False} + else: + res = api.web_sd_upload(upload_file) + # logger.debug(res) + # if res["msg"]: + # st.success("上文件传成功") + # else: + # st.toast("文件上传失败") + + def dialogue_page(api: ApiRequest): global GLOBAL_EXE_CODE_TEXT chat_box.init_session() @@ -60,8 +83,6 @@ def dialogue_page(api: ApiRequest): "知识库问答", "代码知识库问答", "搜索引擎问答", - "工具问答", - "数据分析", "Agent问答" ], on_change=on_mode_change, @@ -76,8 +97,16 @@ def dialogue_page(api: ApiRequest): def on_cb_change(): st.toast(f"已加载代码知识库: {st.session_state.selected_cb}") + cb_details = get_cb_details_by_cb_name(st.session_state.selected_cb) + st.session_state['do_interpret'] = cb_details['do_interpret'] + + # + if "interpreter_file_key" not in st.session_state: + st.session_state["interpreter_file_key"] = 0 not_agent_qa = True + interpreter_file = "" + is_detailed = False if dialogue_mode == "知识库问答": with st.expander("知识库配置", True): kb_list = api.list_knowledge_bases(no_remote_api=True) @@ -101,56 +130,33 @@ def dialogue_page(api: ApiRequest): on_change=on_cb_change, key="selected_cb", ) + + # change do_interpret st.toast(f"已加载代码知识库: {st.session_state.selected_cb}") + cb_details = get_cb_details_by_cb_name(st.session_state.selected_cb) + st.session_state['do_interpret'] = cb_details['do_interpret'] + cb_code_limit = st.number_input("匹配代码条数:", 1, 20, 1) + + search_type_list = ['基于 cypher', '基于标签', '基于描述'] if st.session_state['do_interpret'] == 'YES' \ + else ['基于 cypher', '基于标签'] + + cb_search_type = st.selectbox( + '请选择查询模式:', + search_type_list, + key='cb_search_type' + ) elif dialogue_mode == "搜索引擎问答": with st.expander("搜索引擎配置", True): search_engine = st.selectbox("请选择搜索引擎", SEARCH_ENGINES.keys(), 0) se_top_k = st.number_input("匹配搜索结果条数:", 1, 20, 3) - elif dialogue_mode == "工具问答": - with st.expander("工具军火库", True): - tool_selects = st.multiselect( - '请选择待使用的工具', TOOL_SETS, ["WeatherInfo"]) - - elif dialogue_mode == "数据分析": - with st.expander("沙盒文件管理", False): - def _upload(upload_file): - res = api.web_sd_upload(upload_file) - logger.debug(res) - if res["msg"]: - st.success("上文件传成功") - else: - st.toast("文件上传失败") - - interpreter_file = st.file_uploader( - "上传沙盒文件", - [i for ls in LOADER2EXT_DICT.values() for i in ls], - accept_multiple_files=False, - key="interpreter_file", - ) - - if interpreter_file: - _upload(interpreter_file) - interpreter_file = None - # - files = api.web_sd_list_files() - files = files["data"] - download_file = st.selectbox("选择要处理文件", files, - key="download_file",) - - cols = st.columns(2) - file_url, file_name = api.web_sd_download(download_file) - cols[0].download_button("点击下载", file_url, file_name) - if cols[1].button("点击删除", ): - api.web_sd_delete(download_file) - elif dialogue_mode == "Agent问答": not_agent_qa = False with st.expander("Phase管理", True): choose_phase = st.selectbox( '请选择待使用的执行链路', PHASE_LIST, 0) - is_detailed = st.toggle("返回明细的Agent交互", False) + is_detailed = st.toggle("是否使用明细信息进行agent交互", False) tool_using_on = st.toggle("开启工具使用", PHASE_CONFIGS[choose_phase]["do_using_tool"]) tool_selects = [] if tool_using_on: @@ -181,6 +187,7 @@ def dialogue_page(api: ApiRequest): code_retrieval_on = st.toggle("开启代码检索增强", PHASE_CONFIGS[choose_phase]["do_code_retrieval"]) selected_cb, top_k = None, 1 + cb_search_type = "tag" if code_retrieval_on: with st.expander('代码知识库配置', True): cb_list = api.list_cb(no_remote_api=True) @@ -194,36 +201,41 @@ def dialogue_page(api: ApiRequest): st.toast(f"已加载代码知识库: {st.session_state.selected_cb}") top_k = st.number_input("匹配代码条数:", 1, 20, 1) - with st.expander("沙盒文件管理", False): - def _upload(upload_file): - res = api.web_sd_upload(upload_file) - logger.debug(res) - if res["msg"]: - st.success("上文件传成功") - else: - st.toast("文件上传失败") + cb_details = get_cb_details_by_cb_name(st.session_state.selected_cb) + st.session_state['do_interpret'] = cb_details['do_interpret'] + search_type_list = ['基于 cypher', '基于标签', '基于描述'] if st.session_state['do_interpret'] == 'YES' \ + else ['基于 cypher', '基于标签'] + cb_search_type = st.selectbox( + '请选择查询模式:', + search_type_list, + key='cb_search_type' + ) - interpreter_file = st.file_uploader( - "上传沙盒文件", - [i for ls in LOADER2EXT_DICT.values() for i in ls], - accept_multiple_files=False, - key="interpreter_file", - ) + with st.expander("沙盒文件管理", False): - if interpreter_file: - _upload(interpreter_file) - interpreter_file = None - # - files = api.web_sd_list_files() - files = files["data"] - download_file = st.selectbox("选择要处理文件", files, - key="download_file",) + interpreter_file = st.file_uploader( + "上传沙盒文件", + [i for ls in LOADER2EXT_DICT.values() for i in ls] + ["jpg", "png"], + accept_multiple_files=False, + key=st.session_state.interpreter_file_key, + ) - cols = st.columns(2) - file_url, file_name = api.web_sd_download(download_file) - cols[0].download_button("点击下载", file_url, file_name) - if cols[1].button("点击删除", ): - api.web_sd_delete(download_file) + files = api.web_sd_list_files() + files = files["data"] + download_file = st.selectbox("选择要处理文件", files, + key="download_file",) + + cols = st.columns(3) + file_url, file_name = api.web_sd_download(download_file) + if cols[0].button("点击上传"): + upload2sandbox(interpreter_file, api) + st.session_state["interpreter_file_key"] += 1 + interpreter_file = "" + st.experimental_rerun() + + cols[1].download_button("点击下载", file_url, file_name) + if cols[2].button("点击删除", ): + api.web_sd_delete(download_file) code_interpreter_on = st.toggle("开启代码解释器") and not_agent_qa code_exec_on = st.toggle("自动执行代码") and not_agent_qa @@ -237,7 +249,10 @@ def dialogue_page(api: ApiRequest): codebox_res = None if prompt := st.chat_input(chat_input_placeholder, key="prompt"): - history = get_messages_history(history_len) + upload2sandbox(interpreter_file, api) + logger.debug(f"prompt: {prompt}") + + history = get_messages_history(history_len, is_detailed) chat_box.user_say(prompt) if dialogue_mode == "LLM 对话": chat_box.ai_say("正在思考...") @@ -281,6 +296,7 @@ def dialogue_page(api: ApiRequest): "doc_engine_name": selected_kb, "search_engine_name": search_engine, "code_engine_name": selected_cb, + "cb_search_type": cb_search_type, "top_k": top_k, "score_threshold": score_threshold, "do_search": search_on, @@ -293,21 +309,26 @@ def dialogue_page(api: ApiRequest): "choose_tools": tool_selects, "history_node_list": history_node_list, "isDetailed": is_detailed, + "upload_file": interpreter_file, } text = "" d = {"docs": []} - for idx_count, d in enumerate(api.agent_chat(**input_kargs)): + for idx_count, d in enumerate(api.agent_achat(**input_kargs)): if error_msg := check_error_msg(d): # check whether error occured st.error(error_msg) - text += d["answer"] - if idx_count%20 == 0: - chat_box.update_msg(text, element_index=0) + # logger.debug(f"d: {d['answer']}") + text = d["answer"] + for text_length in range(0, len(text)+1, 10): + chat_box.update_msg(text[:text_length+10], element_index=0, streaming=True) + + GLOBAL_MESSAGE.setdefault("final_contents", {}).setdefault(d.get("answer", ""), []).append(d.get("final_content", "")) for k, v in d["figures"].items(): - logger.debug(f"figure: {k}") if k in text: img_html = "\n\n".format(v) text = text.replace(k, img_html).replace(".png", "") + GLOBAL_MESSAGE.setdefault("figures", {}).setdefault(k, v) + chat_box.update_msg(text, element_index=0, streaming=False, state="complete") # 更新最终的字符串,去除光标 if search_on: chat_box.update_msg("搜索匹配结果:\n\n" + "\n\n".join(d["search_docs"]), element_index=search_on, streaming=False, state="complete") @@ -317,41 +338,6 @@ def dialogue_page(api: ApiRequest): history_node_list.extend([node[0] for node in d.get("related_nodes", [])]) history_node_list = list(set(history_node_list)) st.session_state['history_node_list'] = history_node_list - - elif dialogue_mode == "工具问答": - chat_box.ai_say("正在思考...") - text = "" - r = api.tool_chat(prompt, history, tool_sets=tool_selects) - for t in r: - if error_msg := check_error_msg(t): # check whether error occured - st.error(error_msg) - break - text += t["answer"] - chat_box.update_msg(text) - logger.debug(f"text: {text}") - chat_box.update_msg(text, streaming=False) # 更新最终的字符串,去除光标 - # 判断是否存在代码, 并提高编辑功能,执行功能 - code_text = api.codebox.decode_code_from_text(text) - GLOBAL_EXE_CODE_TEXT = code_text - if code_text and code_exec_on: - codebox_res = api.codebox_chat("```"+code_text+"```", do_code_exe=True) - elif dialogue_mode == "数据分析": - chat_box.ai_say("正在思考...") - text = "" - r = api.data_chat(prompt, history) - for t in r: - if error_msg := check_error_msg(t): # check whether error occured - st.error(error_msg) - break - text += t["answer"] - chat_box.update_msg(text) - logger.debug(f"text: {text}") - chat_box.update_msg(text, streaming=False) # 更新最终的字符串,去除光标 - # 判断是否存在代码, 并提高编辑功能,执行功能 - code_text = api.codebox.decode_code_from_text(text) - GLOBAL_EXE_CODE_TEXT = code_text - if code_text and code_exec_on: - codebox_res = api.codebox_chat("```"+code_text+"```", do_code_exe=True) elif dialogue_mode == "知识库问答": history = get_messages_history(history_len) chat_box.ai_say([ @@ -382,13 +368,14 @@ def dialogue_page(api: ApiRequest): chat_box.ai_say([ f"正在查询代码知识库 `{selected_cb}` ...", - Markdown("...", in_expander=True, title="代码库匹配结果"), + Markdown("...", in_expander=True, title="代码库匹配节点"), ]) text = "" d = {"codes": []} for idx_count, d in enumerate(api.code_base_chat(query=prompt, code_base_name=selected_cb, code_limit=cb_code_limit, history=history, + cb_search_type=cb_search_type, no_remote_api=True)): if error_msg := check_error_msg(d): st.error(error_msg) @@ -396,14 +383,15 @@ def dialogue_page(api: ApiRequest): if idx_count % 10 == 0: # text = replace_lt_gt(text) chat_box.update_msg(text, element_index=0) + # postprocess - # text = replace_lt_gt(text) + text = replace_lt_gt(text) chat_box.update_msg(text, element_index=0, streaming=False) # 更新最终的字符串,去除光标 logger.debug('text={}'.format(text)) chat_box.update_msg("\n".join(d["codes"]), element_index=1, streaming=False, state="complete") # session state update - st.session_state['history_node_list'] = api.codeChat.history_node_list + # st.session_state['history_node_list'] = api.codeChat.history_node_list elif dialogue_mode == "搜索引擎问答": chat_box.ai_say([ @@ -427,6 +415,10 @@ def dialogue_page(api: ApiRequest): if code_text and code_exec_on: codebox_res = api.codebox_chat("```"+code_text+"```", do_code_exe=True) + # 将上传文件清空 + st.session_state["interpreter_file_key"] += 1 + st.experimental_rerun() + if code_interpreter_on: with st.expander("代码编辑执行器", False): code_part = st.text_area("代码片段", code_text, key="code_text") diff --git a/dev_opsgpt/webui/utils.py b/dev_opsgpt/webui/utils.py index 5868fd3..ac17d46 100644 --- a/dev_opsgpt/webui/utils.py +++ b/dev_opsgpt/webui/utils.py @@ -10,7 +10,7 @@ import json import nltk import traceback from loguru import logger -import zipfile + from configs.model_config import ( EMBEDDING_MODEL, @@ -30,10 +30,12 @@ from configs.server_config import SANDBOX_SERVER from dev_opsgpt.utils.server_utils import run_async, iter_over_async from dev_opsgpt.service.kb_api import * from dev_opsgpt.service.cb_api import * -from dev_opsgpt.chat import LLMChat, SearchChat, KnowledgeChat, ToolChat, DataChat, CodeChat, AgentChat +from dev_opsgpt.chat import LLMChat, SearchChat, KnowledgeChat, CodeChat, AgentChat from dev_opsgpt.sandbox import PyCodeBox, CodeBoxResponse from dev_opsgpt.utils.common_utils import file_normalize, get_uploadfile +from dev_opsgpt.codechat.code_crawler.zip_crawler import ZipCrawler + from web_crawler.utils.WebCrawler import WebCrawler nltk.data.path = [NLTK_DATA_PATH] + nltk.data.path @@ -73,11 +75,9 @@ class ApiRequest: self.llmChat = LLMChat() self.searchChat = SearchChat() self.knowledgeChat = KnowledgeChat() - self.toolChat = ToolChat() - self.dataChat = DataChat() self.codeChat = CodeChat() - self.agentChat = AgentChat() + self.codebox = PyCodeBox( remote_url=SANDBOX_SERVER["url"], remote_ip=SANDBOX_SERVER["host"], # "http://localhost", @@ -247,6 +247,18 @@ class ApiRequest: except Exception as e: logger.error(traceback.format_exc()) + def _stream2generator(self, response: str, as_json: bool =False): + ''' + 将api.py中视图函数返回的StreamingResponse转化为同步生成器 + ''' + try: + if as_json and response: + return json.loads(response) + elif response.strip(): + return response + except Exception as e: + logger.error(traceback.format_exc()) + def _httpx_stream2generator( self, response: contextlib._GeneratorContextManager, @@ -372,66 +384,13 @@ class ApiRequest: ) return self._httpx_stream2generator(response, as_json=True) - def tool_chat( - self, - query: str, - history: List[Dict] = [], - tool_sets: List[str] = [], - stream: bool = True, - no_remote_api: bool = None, - ): - ''' - 对应api.py/chat/chat接口 - ''' - if no_remote_api is None: - no_remote_api = self.no_remote_api - - data = { - "query": query, - "history": history, - "tool_sets": tool_sets, - "stream": stream, - } - - if no_remote_api: - response = self.toolChat.chat(**data) - return self._fastapi_stream2generator(response, as_json=True) - else: - response = self.post("/chat/tool_chat", json=data, stream=True) - return self._httpx_stream2generator(response) - - def data_chat( - self, - query: str, - history: List[Dict] = [], - stream: bool = True, - no_remote_api: bool = None, - ): - ''' - 对应api.py/chat/chat接口 - ''' - if no_remote_api is None: - no_remote_api = self.no_remote_api - - data = { - "query": query, - "history": history, - "stream": stream, - } - - if no_remote_api: - response = self.dataChat.chat(**data) - return self._fastapi_stream2generator(response, as_json=True) - else: - response = self.post("/chat/data_chat", json=data, stream=True) - return self._httpx_stream2generator(response) - def code_base_chat( self, query: str, code_base_name: str, code_limit: int = 1, history: List[Dict] = [], + cb_search_type: str = 'tag', stream: bool = True, no_remote_api: bool = None, ): @@ -441,20 +400,31 @@ class ApiRequest: if no_remote_api is None: no_remote_api = self.no_remote_api + cb_search_type = { + '基于 cypher': 'cypher', + '基于标签': 'tag', + '基于描述': 'description', + 'tag': 'tag', + 'description': 'description', + 'cypher': 'cypher' + }.get(cb_search_type, 'tag') + + data = { "query": query, "history": history, "engine_name": code_base_name, "code_limit": code_limit, + "cb_search_type": cb_search_type, "stream": stream, "local_doc_url": no_remote_api, } logger.info('data={}'.format(data)) if no_remote_api: - logger.info('history_node_list before={}'.format(self.codeChat.history_node_list)) + # logger.info('history_node_list before={}'.format(self.codeChat.history_node_list)) response = self.codeChat.chat(**data) - logger.info('history_node_list after={}'.format(self.codeChat.history_node_list)) + # logger.info('history_node_list after={}'.format(self.codeChat.history_node_list)) return self._fastapi_stream2generator(response, as_json=True) else: response = self.post( @@ -486,7 +456,8 @@ class ApiRequest: custom_role_configs = {}, no_remote_api: bool = None, history_node_list: List[str] = [], - isDetailed: bool = False + isDetailed: bool = False, + upload_file: Union[str, Path, bytes] = "", ): ''' 对应api.py/chat/chat接口 @@ -515,7 +486,8 @@ class ApiRequest: "custom_role_configs": custom_role_configs, "choose_tools": choose_tools, "history_node_list": history_node_list, - "isDetailed": isDetailed + "isDetailed": isDetailed, + "upload_file": upload_file } if no_remote_api: response = self.agentChat.chat(**data) @@ -524,6 +496,71 @@ class ApiRequest: response = self.post("/chat/data_chat", json=data, stream=True) return self._httpx_stream2generator(response) + def agent_achat( + self, + query: str, + phase_name: str, + doc_engine_name: str, + code_engine_name: str, + cb_search_type: str, + search_engine_name: str, + top_k: int = 3, + score_threshold: float = 1.0, + history: List[Dict] = [], + stream: bool = True, + local_doc_url: bool = False, + do_search: bool = False, + do_doc_retrieval: bool = False, + do_code_retrieval: bool = False, + do_tool_retrieval: bool = False, + choose_tools: List[str] = [], + custom_phase_configs = {}, + custom_chain_configs = {}, + custom_role_configs = {}, + no_remote_api: bool = None, + history_node_list: List[str] = [], + isDetailed: bool = False, + upload_file: Union[str, Path, bytes] = "", + ): + ''' + 对应api.py/chat/chat接口 + ''' + if no_remote_api is None: + no_remote_api = self.no_remote_api + + data = { + "query": query, + "phase_name": phase_name, + "chain_name": "", + "history": history, + "doc_engine_name": doc_engine_name, + "code_engine_name": code_engine_name, + "cb_search_type": cb_search_type, + "search_engine_name": search_engine_name, + "top_k": top_k, + "score_threshold": score_threshold, + "stream": stream, + "local_doc_url": local_doc_url, + "do_search": do_search, + "do_doc_retrieval": do_doc_retrieval, + "do_code_retrieval": do_code_retrieval, + "do_tool_retrieval": do_tool_retrieval, + "custom_phase_configs": custom_phase_configs, + "custom_chain_configs": custom_chain_configs, + "custom_role_configs": custom_role_configs, + "choose_tools": choose_tools, + "history_node_list": history_node_list, + "isDetailed": isDetailed, + "upload_file": upload_file + } + + if no_remote_api: + for response in self.agentChat.achat(**data): + yield self._stream2generator(response, as_json=True) + else: + response = self.post("/chat/data_chat", json=data, stream=True) + yield self._httpx_stream2generator(response) + def _check_httpx_json_response( self, response: httpx.Response, @@ -851,12 +888,15 @@ class ApiRequest: def web_sd_download(self, filename: str, save_filename: str = None): '''对应file_service/sd_download_file''' save_filename = save_filename or filename - # response = self.get( - # f"/sdfiles/download", - # params={"filename": filename, "save_filename": save_filename} - # ) - key_value_str = f"filename={filename}&save_filename={save_filename}" - return self._parse_url(f"/sdfiles/download?{key_value_str}"), save_filename + response = self.get( + f"/sdfiles/download", + params={"filename": filename, "save_filename": save_filename} + ) + # logger.debug(f"response: {response.json()}") + if filename: + file_content, _ = file_normalize(response.json()["data"]) + return file_content, save_filename + return "", save_filename def web_sd_delete(self, filename: str): '''对应file_service/sd_delete_file''' @@ -872,7 +912,7 @@ class ApiRequest: return self._check_httpx_json_response(response) # code base 相关操作 - def create_code_base(self, cb_name, zip_file, no_remote_api: bool = None,): + def create_code_base(self, cb_name, zip_file, do_interpret: bool, no_remote_api: bool = None,): ''' 创建 code_base @param cb_name: @@ -893,13 +933,11 @@ class ApiRequest: if not os.path.exists(dir): os.makedirs(dir) - # unzip - with zipfile.ZipFile(zip_file, 'r') as z: - z.extractall(raw_code_path) - data = { + "zip_file": zip_file, "cb_name": cb_name, - "code_path": raw_code_path + "code_path": raw_code_path, + "do_interpret": do_interpret } logger.info('create cb data={}'.format(data)) diff --git a/examples/agent_examples/baseTaskPhase_example.py b/examples/agent_examples/baseTaskPhase_example.py new file mode 100644 index 0000000..78f1b4f --- /dev/null +++ b/examples/agent_examples/baseTaskPhase_example.py @@ -0,0 +1,52 @@ +import os, sys, requests + +src_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) +sys.path.append(src_dir) + +from dev_opsgpt.tools import ( + toLangchainTools, get_tool_schema, DDGSTool, DocRetrieval, + TOOL_DICT, TOOL_SETS + ) + +from configs.model_config import * +from dev_opsgpt.connector.phase import BasePhase +from dev_opsgpt.connector.agents import BaseAgent +from dev_opsgpt.connector.chains import BaseChain +from dev_opsgpt.connector.schema import ( + Message, Memory, load_role_configs, load_phase_configs, load_chain_configs + ) +from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS +import importlib + +tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) + + +role_configs = load_role_configs(AGETN_CONFIGS) +chain_configs = load_chain_configs(CHAIN_CONFIGS) +phase_configs = load_phase_configs(PHASE_CONFIGS) + +agent_module = importlib.import_module("dev_opsgpt.connector.agents") + + +phase_name = "baseTaskPhase" +phase = BasePhase(phase_name, + task = None, + phase_config = PHASE_CONFIGS, + chain_config = CHAIN_CONFIGS, + role_config = AGETN_CONFIGS, + do_summary=False, + do_code_retrieval=False, + do_doc_retrieval=True, + do_search=False, + ) + +# round-1 +query_content = "确认本地是否存在employee_data.csv,并查看它有哪些列和数据类型;然后画柱状图" +query = Message( + role_name="human", role_type="user", + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +output_message, _ = phase.step(query) diff --git a/examples/agent_examples/codeChatPhase_example.py b/examples/agent_examples/codeChatPhase_example.py new file mode 100644 index 0000000..1fa2a1c --- /dev/null +++ b/examples/agent_examples/codeChatPhase_example.py @@ -0,0 +1,129 @@ +import os, sys, requests + +src_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) +sys.path.append(src_dir) + +from dev_opsgpt.tools import ( + toLangchainTools, get_tool_schema, DDGSTool, DocRetrieval, + TOOL_DICT, TOOL_SETS + ) + +from configs.model_config import * +from dev_opsgpt.connector.phase import BasePhase +from dev_opsgpt.connector.agents import BaseAgent +from dev_opsgpt.connector.chains import BaseChain +from dev_opsgpt.connector.schema import ( + Message, Memory, load_role_configs, load_phase_configs, load_chain_configs + ) +from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS +import importlib + +tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) + + +role_configs = load_role_configs(AGETN_CONFIGS) +chain_configs = load_chain_configs(CHAIN_CONFIGS) +phase_configs = load_phase_configs(PHASE_CONFIGS) + +agent_module = importlib.import_module("dev_opsgpt.connector.agents") + + +phase_name = "codeChatPhase" +phase = BasePhase(phase_name, + task = None, + phase_config = PHASE_CONFIGS, + chain_config = CHAIN_CONFIGS, + role_config = AGETN_CONFIGS, + do_summary=False, + do_code_retrieval=True, + do_doc_retrieval=False, + do_search=False, + ) + +# 代码一共有多少类 => 基于cypher +# 代码库里有哪些函数,返回5个就行 => 基于cypher +# remove 这个函数是做什么的 => 基于标签 +# 有没有函数已经实现了从字符串删除指定字符串的功能,使用的话可以怎么使用,写个java代码 => 基于描述 +# 有根据我以下的需求用 java 开发一个方法:输入为字符串,将输入中的 .java 字符串给删除掉,然后返回新的字符串 => 基于描述 + +# round-1 +query_content = "代码一共有多少类" +query = Message( + role_name="user", role_type="human", + role_content=query_content, input_query=query_content, origin_query=query_content, + code_engine_name="client", score_threshold=1.0, top_k=3, cb_search_type="cypher" + ) + +output_message1, _ = phase.step(query) + +# round-2 +history = Memory(messages=[ + Message(role_name="user", role_type="human", role_content="代码一共有多少类"), + Message(role_name="ai", role_type="assistant", role_content=output_message1.step_content), + ]) + +query_content = "代码库里有哪些函数,返回5个就行" +query = Message( + role_name="user", role_type="human", + role_content=query_content, input_query=query_content, origin_query=query_content, + code_engine_name="client", score_threshold=1.0, top_k=3, cb_search_type="cypher" + ) +output_message2, _ = phase.step(query) + +# round-3 +history = Memory(messages=[ + Message(role_name="user", role_type="human", role_content="代码一共有多少类"), + Message(role_name="ai", role_type="assistant", role_content=output_message1.step_content), + Message(role_name="user", role_type="human", role_content="代码库里有哪些函数,返回5个就行"), + Message(role_name="ai", role_type="assistant", role_content=output_message2.step_content), + ]) + +query_content = "remove 这个函数是做什么的" +query = Message( + role_name="user", role_type="human", + role_content=query_content, input_query=query_content, origin_query=query_content, + code_engine_name="client", score_threshold=1.0, top_k=3, cb_search_type="tag" + ) +output_message3, _ = phase.step(query) + +# round-4 +history = Memory(messages=[ + Message(role_name="user", role_type="human", role_content="代码一共有多少类"), + Message(role_name="ai", role_type="assistant", role_content=output_message1.step_content), + Message(role_name="user", role_type="human", role_content="代码库里有哪些函数,返回5个就行"), + Message(role_name="ai", role_type="assistant", role_content=output_message2.step_content), + Message(role_name="user", role_type="human", role_content="remove 这个函数是做什么的"), + Message(role_name="ai", role_type="assistant", role_content=output_message3.step_content), + ]) + + +query_content = "有没有函数已经实现了从字符串删除指定字符串的功能,使用的话可以怎么使用,写个java代码" +query = Message( + role_name="user", role_type="human", + role_content=query_content, input_query=query_content, origin_query=query_content, + code_engine_name="client", score_threshold=1.0, top_k=3, cb_search_type="description" + ) +output_message4, _ = phase.step(query) + + +# round-5 +history = Memory(messages=[ + Message(role_name="user", role_type="human", role_content="代码一共有多少类"), + Message(role_name="ai", role_type="assistant", role_content=output_message1.step_content), + Message(role_name="user", role_type="human", role_content="代码库里有哪些函数,返回5个就行"), + Message(role_name="ai", role_type="assistant", role_content=output_message2.step_content), + Message(role_name="user", role_type="human", role_content="remove 这个函数是做什么的"), + Message(role_name="ai", role_type="assistant", role_content=output_message3.step_content), + Message(role_name="user", role_type="human", role_content="有没有函数已经实现了从字符串删除指定字符串的功能,使用的话可以怎么使用,写个java代码"), + Message(role_name="ai", role_type="assistant", role_content=output_message4.step_content), + ]) + +query_content = "有根据我以下的需求用 java 开发一个方法:输入为字符串,将输入中的 .java 字符串给删除掉,然后返回新的字符串" +query = Message( + role_name="user", role_type="human", + role_content=query_content, input_query=query_content, origin_query=query_content, + code_engine_name="client", score_threshold=1.0, top_k=3, cb_search_type="description" + ) +output_message5, _ = phase.step(query) \ No newline at end of file diff --git a/examples/agent_examples/codeReactPhase_example.py b/examples/agent_examples/codeReactPhase_example.py new file mode 100644 index 0000000..22cf560 --- /dev/null +++ b/examples/agent_examples/codeReactPhase_example.py @@ -0,0 +1,52 @@ +import os, sys, requests + +src_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) +sys.path.append(src_dir) + +from dev_opsgpt.tools import ( + toLangchainTools, get_tool_schema, DDGSTool, DocRetrieval, + TOOL_DICT, TOOL_SETS + ) + +from configs.model_config import * +from dev_opsgpt.connector.phase import BasePhase +from dev_opsgpt.connector.agents import BaseAgent +from dev_opsgpt.connector.chains import BaseChain +from dev_opsgpt.connector.schema import ( + Message, Memory, load_role_configs, load_phase_configs, load_chain_configs + ) +from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS +import importlib + +tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) + + +role_configs = load_role_configs(AGETN_CONFIGS) +chain_configs = load_chain_configs(CHAIN_CONFIGS) +phase_configs = load_phase_configs(PHASE_CONFIGS) + +agent_module = importlib.import_module("dev_opsgpt.connector.agents") + + +phase_name = "codeReactPhase" +phase = BasePhase(phase_name, + task = None, + phase_config = PHASE_CONFIGS, + chain_config = CHAIN_CONFIGS, + role_config = AGETN_CONFIGS, + do_summary=False, + do_code_retrieval=False, + do_doc_retrieval=True, + do_search=False, + ) + +# round-1 +query_content = "确认本地是否存在employee_data.csv,并查看它有哪些列和数据类型;然后画柱状图" +query = Message( + role_name="human", role_type="user", + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +output_message, _ = phase.step(query) diff --git a/examples/agent_examples/coedToolReactPhase_example.py b/examples/agent_examples/coedToolReactPhase_example.py new file mode 100644 index 0000000..e39386c --- /dev/null +++ b/examples/agent_examples/coedToolReactPhase_example.py @@ -0,0 +1,59 @@ +import os, sys, requests + +src_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) +# src_dir = os.path.join( +# os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +# ) +sys.path.append(src_dir) + +from dev_opsgpt.tools import ( + toLangchainTools, get_tool_schema, DDGSTool, DocRetrieval, + TOOL_DICT, TOOL_SETS + ) + +from configs.model_config import * +from dev_opsgpt.connector.phase import BasePhase +from dev_opsgpt.connector.agents import BaseAgent +from dev_opsgpt.connector.chains import BaseChain +from dev_opsgpt.connector.schema import ( + Message, load_role_configs, load_phase_configs, load_chain_configs + ) +from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS +import importlib + +print(src_dir) + +# tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) + +TOOL_SETS = [ + "StockInfo", "StockName" + ] + +tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) + + +role_configs = load_role_configs(AGETN_CONFIGS) +chain_configs = load_chain_configs(CHAIN_CONFIGS) +phase_configs = load_phase_configs(PHASE_CONFIGS) + +agent_module = importlib.import_module("dev_opsgpt.connector.agents") + +phase_name = "codeToolReactPhase" +phase = BasePhase(phase_name, + task = None, + phase_config = PHASE_CONFIGS, + chain_config = CHAIN_CONFIGS, + role_config = AGETN_CONFIGS, + do_summary=False, + do_code_retrieval=False, + do_doc_retrieval=True, + do_search=False, + ) + +query_content = "查询贵州茅台的股票代码,并查询截止到当前日期(2023年11月8日)的最近10天的每日时序数据,然后对时序数据画出折线图并分析" + +query = Message(role_name="human", role_type="user", input_query=query_content, role_content=query_content, origin_query=query_content, tools=tools) + +output_message = phase.step(query) \ No newline at end of file diff --git a/examples/agent_examples/docChatPhase_example.py b/examples/agent_examples/docChatPhase_example.py new file mode 100644 index 0000000..58839b8 --- /dev/null +++ b/examples/agent_examples/docChatPhase_example.py @@ -0,0 +1,67 @@ +import os, sys, requests + +src_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) +sys.path.append(src_dir) + +from dev_opsgpt.tools import ( + toLangchainTools, get_tool_schema, DDGSTool, DocRetrieval, + TOOL_DICT, TOOL_SETS + ) + +from configs.model_config import * +from dev_opsgpt.connector.phase import BasePhase +from dev_opsgpt.connector.agents import BaseAgent +from dev_opsgpt.connector.chains import BaseChain +from dev_opsgpt.connector.schema import ( + Message, Memory, load_role_configs, load_phase_configs, load_chain_configs + ) +from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS +import importlib + +tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) + + +role_configs = load_role_configs(AGETN_CONFIGS) +chain_configs = load_chain_configs(CHAIN_CONFIGS) +phase_configs = load_phase_configs(PHASE_CONFIGS) + +agent_module = importlib.import_module("dev_opsgpt.connector.agents") + + +phase_name = "docChatPhase" +phase = BasePhase(phase_name, + task = None, + phase_config = PHASE_CONFIGS, + chain_config = CHAIN_CONFIGS, + role_config = AGETN_CONFIGS, + do_summary=False, + do_code_retrieval=False, + do_doc_retrieval=True, + do_search=False, + ) + +# round-1 +query_content = "langchain有哪些模块" +query = Message( + role_name="user", role_type="human", + role_content=query_content, input_query=query_content, origin_query=query_content, + doc_engine_name="DSADSAD", score_threshold=1.0, top_k=3 + ) + +output_message, _ = phase.step(query) + +# round-2 +history = Memory(messages=[ + Message(role_name="user", role_type="human", role_content="langchain有哪些模块"), + Message(role_name="ai", role_type="assistant", role_content=output_message.step_content), + ]) + +query_content = "提示(prompts)有什么用?" +query = Message( + role_name="human", role_type="user", + role_content=query_content, input_query=query_content, origin_query=query_content, + doc_engine_name="DSADSAD", score_threshold=1.0, top_k=3 + ) +output_message, _ = phase.step(query) \ No newline at end of file diff --git a/examples/agent_examples/metagpt_phase_example.py b/examples/agent_examples/metagpt_phase_example.py new file mode 100644 index 0000000..5c3f72b --- /dev/null +++ b/examples/agent_examples/metagpt_phase_example.py @@ -0,0 +1,41 @@ +import os, sys + +src_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) +sys.path.append(src_dir) + +from configs.model_config import * +from dev_opsgpt.connector.phase import BasePhase +from dev_opsgpt.connector.agents import BaseAgent +from dev_opsgpt.connector.chains import BaseChain +from dev_opsgpt.connector.schema import ( + Message, load_role_configs, load_phase_configs, load_chain_configs + ) +from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS +import importlib + + +role_configs = load_role_configs(AGETN_CONFIGS) +chain_configs = load_chain_configs(CHAIN_CONFIGS) +phase_configs = load_phase_configs(PHASE_CONFIGS) + +agent_module = importlib.import_module("dev_opsgpt.connector.agents") + + +phase_name = "metagpt_code_devlop" +phase = BasePhase(phase_name, + task = None, + phase_config = PHASE_CONFIGS, + chain_config = CHAIN_CONFIGS, + role_config = AGETN_CONFIGS, + do_summary=False, + do_code_retrieval=False, + do_doc_retrieval=True, + do_search=False, + ) + +query_content = "create a snake game" +query = Message(role_name="human", role_type="user", input_query=query_content, role_content=query_content, origin_query=query_content) + +output_message = phase.step(query) diff --git a/examples/agent_examples/searchChatPhase_example.py b/examples/agent_examples/searchChatPhase_example.py new file mode 100644 index 0000000..6d64e6f --- /dev/null +++ b/examples/agent_examples/searchChatPhase_example.py @@ -0,0 +1,67 @@ +import os, sys, requests + +src_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) +sys.path.append(src_dir) + +from dev_opsgpt.tools import ( + toLangchainTools, get_tool_schema, DDGSTool, DocRetrieval, + TOOL_DICT, TOOL_SETS + ) + +from configs.model_config import * +from dev_opsgpt.connector.phase import BasePhase +from dev_opsgpt.connector.agents import BaseAgent +from dev_opsgpt.connector.chains import BaseChain +from dev_opsgpt.connector.schema import ( + Message, Memory, load_role_configs, load_phase_configs, load_chain_configs + ) +from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS +import importlib + +tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) + + +role_configs = load_role_configs(AGETN_CONFIGS) +chain_configs = load_chain_configs(CHAIN_CONFIGS) +phase_configs = load_phase_configs(PHASE_CONFIGS) + +agent_module = importlib.import_module("dev_opsgpt.connector.agents") + + +phase_name = "searchChatPhase" +phase = BasePhase(phase_name, + task = None, + phase_config = PHASE_CONFIGS, + chain_config = CHAIN_CONFIGS, + role_config = AGETN_CONFIGS, + do_summary=False, + do_code_retrieval=False, + do_doc_retrieval=False, + do_search=True, + ) + +# round-1 +query_content = "美国当前总统是谁?" +query = Message( + role_name="user", role_type="human", + role_content=query_content, input_query=query_content, origin_query=query_content, + search_engine_name="duckduckgo", score_threshold=1.0, top_k=3 + ) + +output_message, _ = phase.step(query) + +# round-2 +history = Memory(messages=[ + Message(role_name="user", role_type="human", role_content="美国当前总统是谁?"), + Message(role_name="ai", role_type="assistant", role_content=output_message.step_content), + ]) + +query_content = "美国上一任总统是谁,两个人有什么关系没?" +query = Message( + role_name="human", role_type="user", + role_content=query_content, input_query=query_content, origin_query=query_content, + search_engine_name="duckduckgo", score_threshold=1.0, top_k=3 + ) +output_message, _ = phase.step(query) \ No newline at end of file diff --git a/examples/agent_examples/toolReactPhase_example.py b/examples/agent_examples/toolReactPhase_example.py new file mode 100644 index 0000000..00136d2 --- /dev/null +++ b/examples/agent_examples/toolReactPhase_example.py @@ -0,0 +1,53 @@ +import os, sys, requests + +src_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) +sys.path.append(src_dir) + +from dev_opsgpt.tools import ( + toLangchainTools, get_tool_schema, DDGSTool, DocRetrieval, + TOOL_DICT, TOOL_SETS + ) + +from configs.model_config import * +from dev_opsgpt.connector.phase import BasePhase +from dev_opsgpt.connector.agents import BaseAgent +from dev_opsgpt.connector.chains import BaseChain +from dev_opsgpt.connector.schema import ( + Message, Memory, load_role_configs, load_phase_configs, load_chain_configs + ) +from dev_opsgpt.connector.configs import AGETN_CONFIGS, CHAIN_CONFIGS, PHASE_CONFIGS +import importlib + + +# init configs +role_configs = load_role_configs(AGETN_CONFIGS) +chain_configs = load_chain_configs(CHAIN_CONFIGS) +phase_configs = load_phase_configs(PHASE_CONFIGS) + +agent_module = importlib.import_module("dev_opsgpt.connector.agents") + +# +phase_name = "toolReactPhase" +phase = BasePhase(phase_name, + task = None, + phase_config = PHASE_CONFIGS, + chain_config = CHAIN_CONFIGS, + role_config = AGETN_CONFIGS, + do_summary=False, + do_code_retrieval=False, + do_doc_retrieval=True, + do_search=False, + ) + +# round-1 +tools = toLangchainTools([TOOL_DICT[i] for i in TOOL_SETS if i in TOOL_DICT]) + +query_content = "帮我确认下127.0.0.1这个服务器的在10点是否存在异常,请帮我判断一下" +query = Message( + role_name="huma", role_type="user", tools=tools, + role_content=query_content, input_query=query_content, origin_query=query_content, + ) + +output_message, _ = phase.step(query) diff --git a/examples/docker_start.sh b/examples/docker_start.sh deleted file mode 100644 index 8526460..0000000 --- a/examples/docker_start.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -set -x - -CONTAINER_NAME=devopsgpt_default -IMAGES=devopsgpt:pypy3 -WORK_DIR=$PWD - -docker stop $CONTAINER_NAME -docker rm $CONTAINER_NAME -EXTERNAL_PORT=5050 - -# linux start -# docker run -it -p 5050:5050 --name $CONTAINER_NAME $IMAGES bash - -# windows start -winpty docker run -it -d -p $EXTERNAL_PORT:5050 --name $CONTAINER_NAME $IMAGES bash diff --git a/examples/start.py b/examples/start.py index cd6f52f..888dcca 100644 --- a/examples/start.py +++ b/examples/start.py @@ -12,7 +12,7 @@ from configs.model_config import USE_FASTCHAT, JUPYTER_WORK_PATH from configs.server_config import ( NO_REMOTE_API, SANDBOX_SERVER, SANDBOX_IMAGE_NAME, SANDBOX_CONTRAINER_NAME, WEBUI_SERVER, API_SERVER, SDFILE_API_SERVER, CONTRAINER_NAME, IMAGE_NAME, DOCKER_SERVICE, - DEFAULT_BIND_HOST, + DEFAULT_BIND_HOST, NEBULA_GRAPH_SERVER ) @@ -44,6 +44,18 @@ def check_docker(client, container_name, do_stop=False): if i.name == container_name: if do_stop: container = i + + if container_name == CONTRAINER_NAME and i.status == 'running': + # wrap up db + logger.info(f'inside {container_name}') + # cp nebula data + res = container.exec_run('''sh chatbot/dev_opsgpt/utils/nebula_cp.sh''') + logger.info(f'cp res={res}') + + # stop nebula service + res = container.exec_run('''/usr/local/nebula/scripts/nebula.service stop all''') + logger.info(f'stop res={res}') + container.stop() container.remove() return True @@ -56,6 +68,7 @@ def start_docker(client, script_shs, ports, image_name, container_name, mounts=N command="bash", mounts=mounts, name=container_name, + mem_limit="8g", # device_requests=[DeviceRequest(count=-1, capabilities=[['gpu']])], # network_mode="host", ports=ports, @@ -163,12 +176,23 @@ def start_api_service(sandbox_host=DEFAULT_BIND_HOST): f"{API_SERVER['docker_port']}/tcp": f"{API_SERVER['port']}/tcp", f"{WEBUI_SERVER['docker_port']}/tcp": f"{WEBUI_SERVER['port']}/tcp", f"{SDFILE_API_SERVER['docker_port']}/tcp": f"{SDFILE_API_SERVER['port']}/tcp", + f"{NEBULA_GRAPH_SERVER['docker_port']}/tcp": f"{NEBULA_GRAPH_SERVER['port']}/tcp" } mounts = [mount, mount_database, mount_code_database] script_shs = [ "mkdir -p /home/user/logs", - "pip install jsonref", - "pip install javalang", + ''' + if [ -d "/home/user/chatbot/data/nebula_data/data/meta" ]; then + cp -r /home/user/chatbot/data/nebula_data/data /usr/local/nebula/ + fi + ''', + "/usr/local/nebula/scripts/nebula.service start all", + "/usr/local/nebula/scripts/nebula.service status all", + "sleep 2", + '''curl -X PUT -H "Content-Type: application/json" -d'{"heartbeat_interval_secs":"2"}' -s "http://127.0.0.1:19559/flags"''', + '''curl -X PUT -H "Content-Type: application/json" -d'{"heartbeat_interval_secs":"2"}' -s "http://127.0.0.1:19669/flags"''', + '''curl -X PUT -H "Content-Type: application/json" -d'{"heartbeat_interval_secs":"2"}' -s "http://127.0.0.1:19779/flags"''', + "nohup python chatbot/dev_opsgpt/service/sdfile_api.py > /home/user/logs/sdfile_api.log 2>&1 &", f"export DUCKDUCKGO_PROXY=socks5://host.docker.internal:13659 && export SANDBOX_HOST={sandbox_host} &&\ nohup python chatbot/dev_opsgpt/service/api.py > /home/user/logs/api.log 2>&1 &", @@ -209,15 +233,16 @@ def start_api_service(sandbox_host=DEFAULT_BIND_HOST): if __name__ == "__main__": start_sandbox_service() + sandbox_host = DEFAULT_BIND_HOST if SANDBOX_SERVER["do_remote"]: client = docker.from_env() containers = client.containers.list(all=True) - sandbox_host = DEFAULT_BIND_HOST for container in containers: container_a_info = client.containers.get(container.id) if container_a_info.name == SANDBOX_CONTRAINER_NAME: container1_networks = container.attrs['NetworkSettings']['Networks'] sandbox_host = container1_networks.get(network_name)["IPAddress"] break - start_api_service() + start_api_service(sandbox_host) + diff --git a/examples/start_sandbox.py b/examples/start_sandbox.py deleted file mode 100644 index 4ba53ff..0000000 --- a/examples/start_sandbox.py +++ /dev/null @@ -1,49 +0,0 @@ -import docker, sys, os, time, requests - -from loguru import logger - -src_dir = os.path.join( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -) -sys.path.append(src_dir) - -from configs.server_config import SANDBOX_SERVER, SANDBOX_IMAGE_NAME, SANDBOX_CONTRAINER_NAME - - -if SANDBOX_SERVER["do_remote"]: - client = docker.from_env() - for i in client.containers.list(all=True): - if i.name == SANDBOX_CONTRAINER_NAME: - container = i - container.stop() - container.remove() - break - # 启动容器 - logger.info("start ot init container & notebook") - container = client.containers.run( - image=SANDBOX_IMAGE_NAME, - command="bash", - name=SANDBOX_CONTRAINER_NAME, - ports={f"{SANDBOX_SERVER['docker_port']}/tcp": SANDBOX_SERVER["port"]}, - stdin_open=True, - detach=True, - tty=True, - ) - - # 启动notebook - exec_command = container.exec_run("bash jupyter_start.sh") - - # 判断notebook是否启动 - retry_nums = 3 - while retry_nums>0: - response = requests.get(f"http://localhost:{SANDBOX_SERVER['port']}", timeout=270) - if response.status_code == 200: - logger.info("container & notebook init success") - break - else: - retry_nums -= 1 - logger.info(client.containers.list()) - logger.info("wait container running ...") - time.sleep(5) -else: - logger.info("启动local的notebook环境支持代码执行") diff --git a/examples/start_service_docker.py b/examples/start_service_docker.py deleted file mode 100644 index 017ff85..0000000 --- a/examples/start_service_docker.py +++ /dev/null @@ -1,68 +0,0 @@ -import docker, sys, os, time, requests -from docker.types import Mount - -from loguru import logger - -src_dir = os.path.join( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -) -sys.path.append(src_dir) - -from configs.server_config import WEBUI_SERVER, API_SERVER, SDFILE_API_SERVER, CONTRAINER_NAME, IMAGE_NAME -from configs.model_config import USE_FASTCHAT - - - -logger.info(f"IMAGE_NAME: {IMAGE_NAME}, CONTRAINER_NAME: {CONTRAINER_NAME}, ") - - -client = docker.from_env() -for i in client.containers.list(all=True): - if i.name == CONTRAINER_NAME: - container = i - container.stop() - container.remove() - break - - - -# 启动容器 -logger.info("start service") - -mount = Mount( - type='bind', - source=src_dir, - target='/home/user/chatbot/', - read_only=True # 如果需要只读访问,将此选项设置为True -) - -container = client.containers.run( - image=IMAGE_NAME, - command="bash", - mounts=[mount], - name=CONTRAINER_NAME, - ports={ - f"{WEBUI_SERVER['docker_port']}/tcp": API_SERVER['port'], - f"{API_SERVER['docker_port']}/tcp": WEBUI_SERVER['port'], - f"{SDFILE_API_SERVER['docker_port']}/tcp": SDFILE_API_SERVER['port'], - }, - stdin_open=True, - detach=True, - tty=True, -) - -# 启动notebook -exec_command = container.exec_run("bash jupyter_start.sh") -# -exec_command = container.exec_run("cd /homse/user/chatbot && nohup python devops_gpt/service/sdfile_api.py > /homse/user/logs/sdfile_api.log &") -# -exec_command = container.exec_run("cd /homse/user/chatbot && nohup python devops_gpt/service/api.py > /homse/user/logs/api.log &") - -if USE_FASTCHAT: - # 启动fastchat的服务 - exec_command = container.exec_run("cd /homse/user/chatbot && nohup python devops_gpt/service/llm_api.py > /homse/user/logs/llm_api.log &") -# -exec_command = container.exec_run("cd /homse/user/chatbot/examples && nohup bash start_webui.sh > /homse/user/logs/start_webui.log &") - - - diff --git a/examples/start_webui.sh b/examples/start_webui.sh deleted file mode 100644 index 1e06295..0000000 --- a/examples/start_webui.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - - -set -e - -# python ../dev_opsgpt/service/llm_api.py - -# 启动独立的沙箱环境 -python start_sandbox.py - -# python ../dev_opsgpt/service/llm_api.py -streamlit run webui.py diff --git a/examples/stop.py b/examples/stop.py index c87991d..9d1c495 100644 --- a/examples/stop.py +++ b/examples/stop.py @@ -10,7 +10,6 @@ from configs.server_config import ( SANDBOX_CONTRAINER_NAME, CONTRAINER_NAME, SANDBOX_SERVER, DOCKER_SERVICE ) - from start import check_docker, check_process try: diff --git a/examples/stop_sandbox.py b/examples/stop_sandbox.py deleted file mode 100644 index e243f8b..0000000 --- a/examples/stop_sandbox.py +++ /dev/null @@ -1,32 +0,0 @@ -import docker, sys, os, time, requests - -from loguru import logger - -src_dir = os.path.join( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -) -sys.path.append(src_dir) - -from configs.server_config import CONTRAINER_NAME, SANDBOX_SERVER - - -if SANDBOX_SERVER["do_remote"]: - # stop and remove the container - client = docker.from_env() - for i in client.containers.list(all=True): - if i.name == CONTRAINER_NAME: - container = i - container.stop() - container.remove() - break -else: - # stop local - import psutil - for process in psutil.process_iter(["pid", "name", "cmdline"]): - # check process name contains "jupyter" and port=xx - if f"port={SANDBOX_SERVER['port']}" in str(process.info["cmdline"]).lower() and \ - "jupyter" in process.info['name'].lower(): - - logger.warning(f"port={SANDBOX_SERVER['port']}, {process.info}") - # 关闭进程 - process.terminate() diff --git a/examples/stop_webui.sh b/examples/stop_webui.sh deleted file mode 100644 index 60a5ff8..0000000 --- a/examples/stop_webui.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -# stop sandbox -python stop_sandbox.py diff --git a/requirements.txt b/requirements.txt index 8c5213e..168a06e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -42,3 +42,6 @@ streamlit-aggrid>=0.3.4.post3 httpx~=0.24.1 javalang==0.13.0 +jsonref==1.1.0 +chromadb==0.4.17 +nebula3-python==3.1.0 diff --git a/sources/docs_imgs/devops-chatbot-module-v2.png b/sources/docs_imgs/devops-chatbot-module-v2.png new file mode 100644 index 0000000000000000000000000000000000000000..b905e86a98d329510aae5e38cb47aaa5ec99eec0 GIT binary patch literal 665246 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�c5FtcO%pwigGpWK>JGx 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%0AnUD9(n8pNmN7CAOD$O-OS36 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 z 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

>gr*~pz=Xt|9_im~_hNv}}?=0Fx zh**LC^mEr9<{5AS4EQaz@nzfUeYLY}g$KEzhB`t{4j9x_OSSP@vD|r@yzD$ZWe=21 zCA)W=>n}jZ2wqQR>m$Z>fN^DIpz5L5fT)_f{!mgsBA^`|@eY(|} z;A2h`NMI0Iv~*l#ZYd9^=EU(aVap%+Tkb90^=k zVhD)YqwoD4u~xppduRB^-$49jCAE}CiWUzF$`a$7Uuzwi9IijOx#sFHR8^{S4~klu?rk~;kAxF#?VWprTzR(M z&%Jc8TJCPR$v}7a&4Hnbn+Jrs7#PmFYUAhj5ckEBb6-NByX(yOK$oreMu9F8+nVj| zrp+}?G!!-;PpnS9m924t&8NC-lXK>&6P3y~P{4I_NfE$AK#~xGyC;}}`Ry370pY$u zSdW2T_K3Z57Tk`->EYVP{v671x8@ZU1|Zx;%8eJk4Mj3dn_{fx^gMB|W6=23ScAd@ zx`^^^d94%ed5H}8#Nzdj9maf|Bd>)+w+0$KmN>-cEdS){8MPB;pKBV&2X5cwzTypG^V#;qfD-1~j=fyH@7#R6706a!H`lhW*q*ZTNIzQl zRL!+=hx3caFWrIdRU)MUPi&|U+`(Ium6n993Zy;VU$>LZS08U%g3)u&DVT?nUG&Yu z>@1T=gxxo;YtKTJ<}DWPp^4av>rul5la!d-jOyFp^=u*UKw(B8QT< zDae$Ka}D+K#6X5kMlT2{k>wAbLAxwJ)Tf{k)m74gtxuu}SO=7krGX34mT~vZns8eq z|7S*IAkcjZm{~skDj;e=Cd!H(Zpl_p1VTCm!oFUkTVM;VGG~A=Gs&EhspaIMI1M*L zAf_XF3F+#Q)hR~)=ni=s(j{iGr&`LrsjwWt8BskL#afQ>Zf+uiWHRba1vY|LCsHfJ z7yUuOMc)~c0v9~oktY!wU(HN%3o@N<10?I{$Pv@E^=4`Wsed7VckjW=pCxnG`6ovX zG)8bS(b+C$uYUJ$KxcWFn+PgPGg8{{xP+3o@IX+zr6R#jgs|NWxDM(uI_F<($a9d+}%9VdgX*7;p&>YhPz{n z6VM4axGuQ~|@0K|+dmF~5ry38k;zr;D-STj+J>jmdX_|-w-CCg8 zb!K|4oCLa+9wj2sEq9z&petEHylegS+<3O@%w)1?4E-7HJiQezT3sUtsR~0->#IFy z;h=3k)>U%8f+_&m^)v-7YfD)R34o!%P+cv@9c@~g+l~PM1{yTDVtrj@JBKe2K;bXX zF5YRX{fiHoKJY_RR>^%}IcS%Z+&Zg(o`4Y78Ujd*v#9yLV$j1FkOJf^9-#_}Mn