From 2d726185f869247702917aed58c217b95fd35a2c Mon Sep 17 00:00:00 2001 From: shanshi Date: Thu, 28 Mar 2024 20:12:36 +0800 Subject: [PATCH] [feature](webui) --- .gitignore | 2 + README.md | 63 ++---- README_en.md | 56 +---- coagent/base_configs/env_config.py | 13 +- .../codebase_handler/codebase_handler.py | 1 + coagent/connector/memory_manager.py | 6 +- coagent/sandbox/pycodebox.py | 5 +- coagent/utils/code2doc_util.py | 6 +- configs/default_config.py | 7 +- configs/model_config.py.example | 92 ++++---- configs/server_config.py.example | 29 ++- .../codeChatPhaseLocal_example.py | 46 ++-- .../agent_examples/searchChatPhase_example.py | 1 - examples/model_workers/__init__.py | 9 + examples/model_workers/base.py | 3 +- examples/start.py | 32 ++- examples/start.sh | 7 + examples/stop.py | 26 ++- examples/webui/document.py | 2 +- examples/webui_config.py | 208 ++++++++++++++++++ sources/docs_imgs/webui_config.png | Bin 0 -> 56204 bytes tests/chains_test.py | 2 +- tests/openai_test.py | 44 ++-- tests/sandbox_test.py | 3 +- 24 files changed, 428 insertions(+), 235 deletions(-) create mode 100644 examples/start.sh create mode 100644 examples/webui_config.py create mode 100644 sources/docs_imgs/webui_config.png diff --git a/.gitignore b/.gitignore index 3a4c26b..6bc9886 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ tests *egg-info build dist +package.sh +local_config.json \ No newline at end of file diff --git a/README.md b/README.md index 57f540c..8853fef 100644 --- a/README.md +++ b/README.md @@ -123,60 +123,23 @@ cd codefuse-chatbot pip install -r requirements.txt ``` -2、基础配置 - -```bash -# 修改服务启动的基础配置 -cd configs -cp model_config.py.example model_config.py -cp server_config.py.example server_config.py - -# model_config#11~12 若需要使用openai接口,openai接口key -os.environ["OPENAI_API_KEY"] = "sk-xxx" -# 可自行替换自己需要的api_base_url -os.environ["API_BASE_URL"] = "https://api.openai.com/v1" - -# vi model_config#LLM_MODEL 你需要选择的语言模型 -LLM_MODEL = "gpt-3.5-turbo" -LLM_MODELs = ["gpt-3.5-turbo"] - -# vi model_config#EMBEDDING_MODEL 你需要选择的私有化向量模型 -EMBEDDING_ENGINE = 'model' -EMBEDDING_MODEL = "text2vec-base" - -# vi model_config#embedding_model_dict 修改成你的本地路径,如果能直接连接huggingface则无需修改 -# 若模型地址为: -model_dir: ~/codefuse-chatbot/embedding_models/shibing624/text2vec-base-chinese -# 配置如下 -"text2vec-base": "shibing624/text2vec-base-chinese", - -# vi server_config#8~14, 推荐采用容器启动服务 -DOCKER_SERVICE = True -# 是否采用容器沙箱 -SANDBOX_DO_REMOTE = True -# 是否采用api服务来进行 -NO_REMOTE_API = True -``` - -3、启动服务 - -默认只启动webui相关服务,未启动fastchat(可选)。 -```bash -# 若需要支撑codellama-34b-int4模型,需要给fastchat打一个补丁 -# cp examples/gptq.py ~/site-packages/fastchat/modules/gptq.py -# examples/llm_api.py#258 修改为 kwargs={"gptq_wbits": 4}, - -# start llm-service(可选) -python examples/llm_api.py -``` -更多LLM接入方法见[更多细节...](sources/readme_docs/fastchat.md) -
- +2、启动服务 ```bash # 完成server_config.py配置后,可一键启动 cd examples -python start.py +bash start.sh +# 开始在页面进行配置即可 ``` +
+ 图片 +
+ + +或者通过`start.py`进行启动[老版启动方式](sources/readme_docs/start.md) +更多LLM接入方法见[更多细节...](sources/readme_docs/fastchat.md) +
+ + ## 贡献指南 非常感谢您对 Codefuse 项目感兴趣,我们非常欢迎您对 Codefuse 项目的各种建议、意见(包括批评)、评论和贡献。 diff --git a/README_en.md b/README_en.md index 1d7e5b9..b5a97bb 100644 --- a/README_en.md +++ b/README_en.md @@ -146,57 +146,23 @@ git lfs clone https://huggingface.co/THUDM/chatglm2-6b git lfs clone https://huggingface.co/shibing624/text2vec-base-chinese ``` -4. Basic Configuration - -```bash -# Modify the basic configuration for service startup -cd configs -cp model_config.py.example model_config.py -cp server_config.py.example server_config.py - -# model_config#11~12 If you need to use the openai interface, openai interface key -os.environ["OPENAI_API_KEY"] = "sk-xxx" -# You can replace the api_base_url yourself -os.environ["API_BASE_URL"] = "https://api.openai.com/v1" - -# vi model_config#105 You need to choose the language model -LLM_MODEL = "gpt-3.5-turbo" - -# vi model_config#43 You need to choose the vector model -EMBEDDING_MODEL = "text2vec-base" - -# vi model_config#25 Modify to your local path, if you can directly connect to huggingface, no modification is needed -"text2vec-base": "shibing624/text2vec-base-chinese", - -# vi server_config#8~14, it is recommended to start the service using containers. -DOCKER_SERVICE = True -# Whether to use container sandboxing is up to your specific requirements and preferences -SANDBOX_DO_REMOTE = True -# Whether to use api-service to use chatbot -NO_REMOTE_API = True -``` - -5. Start the Service - -By default, only webui related services are started, and fastchat is not started (optional). -```bash -# if use codellama-34b-int4, you should replace fastchat's gptq.py -# cp examples/gptq.py ~/site-packages/fastchat/modules/gptq.py -# examples/llm_api.py#258 => kwargs={"gptq_wbits": 4}, - -# start llm-service(可选) -python examples/llm_api.py -``` -More details about accessing LLM Moldes[More Details...](sources/readme_docs/fastchat.md) -
+4. Start the Service ```bash # After configuring server_config.py, you can start with just one click. cd examples -bash start_webui.sh +bash start.sh +# you can config your llm model and embedding model ``` +
+ 图片 +
-## 贡献指南 +Or `python start.py` by [old version to start](sources/readme_docs/start-en.md) +More details about accessing LLM Moldes[More Details...](sources/readme_docs/fastchat.md) +
+ +## Contribution Thank you for your interest in the Codefuse project. We warmly welcome any suggestions, opinions (including criticisms), comments, and contributions to the Codefuse project. Your suggestions, opinions, and comments on Codefuse can be directly submitted through GitHub Issues. diff --git a/coagent/base_configs/env_config.py b/coagent/base_configs/env_config.py index e931131..8005dcd 100644 --- a/coagent/base_configs/env_config.py +++ b/coagent/base_configs/env_config.py @@ -1,5 +1,6 @@ import os import platform +from loguru import logger system_name = platform.system() executable_path = os.getcwd() @@ -7,8 +8,8 @@ executable_path = os.getcwd() # 日志存储路径 LOG_PATH = os.environ.get("LOG_PATH", None) or os.path.join(executable_path, "logs") -# 知识库默认存储路径 -SOURCE_PATH = os.environ.get("SOURCE_PATH", None) or os.path.join(executable_path, "sources") +# # 知识库默认存储路径 +# SOURCE_PATH = os.environ.get("SOURCE_PATH", None) or os.path.join(executable_path, "sources") # 知识库默认存储路径 KB_ROOT_PATH = os.environ.get("KB_ROOT_PATH", None) or os.path.join(executable_path, "knowledge_base") @@ -16,8 +17,8 @@ KB_ROOT_PATH = os.environ.get("KB_ROOT_PATH", None) or os.path.join(executable_p # 代码库默认存储路径 CB_ROOT_PATH = os.environ.get("CB_ROOT_PATH", None) or os.path.join(executable_path, "code_base") -# nltk 模型存储路径 -NLTK_DATA_PATH = os.environ.get("NLTK_DATA_PATH", None) or os.path.join(executable_path, "nltk_data") +# # nltk 模型存储路径 +# NLTK_DATA_PATH = os.environ.get("NLTK_DATA_PATH", None) or os.path.join(executable_path, "nltk_data") # 代码存储路径 JUPYTER_WORK_PATH = os.environ.get("JUPYTER_WORK_PATH", None) or os.path.join(executable_path, "jupyter_work") @@ -31,8 +32,8 @@ NEBULA_PATH = os.environ.get("NEBULA_PATH", None) or os.path.join(executable_pat # CHROMA 存储路径 CHROMA_PERSISTENT_PATH = os.environ.get("CHROMA_PERSISTENT_PATH", None) or os.path.join(executable_path, "data/chroma_data") -for _path in [LOG_PATH, SOURCE_PATH, KB_ROOT_PATH, CB_ROOT_PATH, NLTK_DATA_PATH, JUPYTER_WORK_PATH, WEB_CRAWL_PATH, NEBULA_PATH, CHROMA_PERSISTENT_PATH]: - if not os.path.exists(_path): +for _path in [LOG_PATH, KB_ROOT_PATH, CB_ROOT_PATH, JUPYTER_WORK_PATH, WEB_CRAWL_PATH, NEBULA_PATH, CHROMA_PERSISTENT_PATH]: + if not os.path.exists(_path) and int(os.environ.get("do_create_dir", True)): os.makedirs(_path, exist_ok=True) # 数据库默认存储路径。 diff --git a/coagent/codechat/codebase_handler/codebase_handler.py b/coagent/codechat/codebase_handler/codebase_handler.py index 2af52c6..601cae1 100644 --- a/coagent/codechat/codebase_handler/codebase_handler.py +++ b/coagent/codechat/codebase_handler/codebase_handler.py @@ -101,6 +101,7 @@ class CodeBaseHandler: # get KG info if self.nh: + time.sleep(10) # aviod nebula staus didn't complete stat = self.nh.get_stat() vertices_num, edges_num = stat['vertices'], stat['edges'] else: diff --git a/coagent/connector/memory_manager.py b/coagent/connector/memory_manager.py index a7d8436..3ccd698 100644 --- a/coagent/connector/memory_manager.py +++ b/coagent/connector/memory_manager.py @@ -310,7 +310,8 @@ class LocalMemoryManager(BaseMemoryManager): # save_to_json_file(memory_messages, file_path) - def load(self, load_dir: str = "./") -> Memory: + def load(self, load_dir: str = None) -> Memory: + load_dir = load_dir or self.kb_root_path file_path = os.path.join(load_dir, f"{self.user_name}/{self.unique_name}/{self.memory_type}/converation.jsonl") uuid_name = "_".join([self.user_name, self.unique_name, self.memory_type]) @@ -398,6 +399,7 @@ class LocalMemoryManager(BaseMemoryManager): def embedding_retrieval(self, text: str, top_k=1, score_threshold=1.0, user_name: str = "default", **kwargs) -> List[Message]: if text is None: return [] vb_name = f"{user_name}/{self.unique_name}/{self.memory_type}" + # logger.debug(f"vb_name={vb_name}") vb = KBServiceFactory.get_service(vb_name, "faiss", self.embed_config, self.kb_root_path) docs = vb.search_docs(text, top_k=top_k, score_threshold=score_threshold) return [Message(**doc.metadata) for doc, score in docs] @@ -405,11 +407,13 @@ class LocalMemoryManager(BaseMemoryManager): def text_retrieval(self, text: str, user_name: str = "default", **kwargs) -> List[Message]: if text is None: return [] uuid_name = "_".join([user_name, self.unique_name, self.memory_type]) + # logger.debug(f"uuid_name={uuid_name}") return self._text_retrieval_from_cache(self.recall_memory_dict[uuid_name].messages, text, score_threshold=0.3, topK=5, **kwargs) def datetime_retrieval(self, datetime: str, text: str = None, n: int = 5, user_name: str = "default", **kwargs) -> List[Message]: if datetime is None: return [] uuid_name = "_".join([user_name, self.unique_name, self.memory_type]) + # logger.debug(f"uuid_name={uuid_name}") return self._datetime_retrieval_from_cache(self.recall_memory_dict[uuid_name].messages, datetime, text, n, **kwargs) def _text_retrieval_from_cache(self, messages: List[Message], text: str = None, score_threshold=0.3, topK=5, tag_topK=5, **kwargs) -> List[Message]: diff --git a/coagent/sandbox/pycodebox.py b/coagent/sandbox/pycodebox.py index 39a4ab9..b1dc189 100644 --- a/coagent/sandbox/pycodebox.py +++ b/coagent/sandbox/pycodebox.py @@ -7,7 +7,7 @@ from websocket import create_connection from websockets.client import WebSocketClientProtocol, ClientConnection from websockets.exceptions import ConnectionClosedError -# from configs.model_config import JUPYTER_WORK_PATH +from coagent.base_configs.env_config import JUPYTER_WORK_PATH from .basebox import BaseBox, CodeBoxResponse, CodeBoxStatus @@ -21,7 +21,7 @@ class PyCodeBox(BaseBox): remote_ip: str = "http://127.0.0.1", remote_port: str = "5050", token: str = "mytoken", - jupyter_work_path: str = "", + jupyter_work_path: str = JUPYTER_WORK_PATH, do_code_exe: bool = False, do_remote: bool = False, do_check_net: bool = True, @@ -30,7 +30,6 @@ class PyCodeBox(BaseBox): super().__init__(remote_url, remote_ip, remote_port, token, do_code_exe, do_remote) self.enter_status = True self.do_check_net = do_check_net - self.use_stop = use_stop self.jupyter_work_path = jupyter_work_path # asyncio.run(self.astart()) self.start() diff --git a/coagent/utils/code2doc_util.py b/coagent/utils/code2doc_util.py index 11cffd4..7e67923 100644 --- a/coagent/utils/code2doc_util.py +++ b/coagent/utils/code2doc_util.py @@ -70,7 +70,8 @@ def encode2md(data, md_format): return md_dict -method_text_md = '''> {function_name} +method_text_md = ''' +> {function_name} | Column Name | Content | |-----------------|-----------------| @@ -79,7 +80,8 @@ method_text_md = '''> {function_name} | Return type | {ReturnType} | ''' -class_text_md = '''> {code_path} +class_text_md = ''' +> {code_path} Bases: {ClassBase} diff --git a/configs/default_config.py b/configs/default_config.py index 3d71d89..e375d9e 100644 --- a/configs/default_config.py +++ b/configs/default_config.py @@ -22,11 +22,14 @@ JUPYTER_WORK_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath WEB_CRAWL_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "knowledge_base") # NEBULA_DATA存储路径 NEBULA_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data/nebula_data") - +# 语言模型存储路径 +LOCAL_LLM_MODEL_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "llm_models") +# 向量模型存储路径 +LOCAL_EM_MODEL_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "embedding_models") # CHROMA 存储路径 CHROMA_PERSISTENT_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data/chroma_data") -for _path in [LOG_PATH, SOURCE_PATH, KB_ROOT_PATH, CB_ROOT_PATH, NLTK_DATA_PATH, JUPYTER_WORK_PATH, WEB_CRAWL_PATH, NEBULA_PATH, CHROMA_PERSISTENT_PATH]: +for _path in [LOG_PATH, SOURCE_PATH, KB_ROOT_PATH, CB_ROOT_PATH, NLTK_DATA_PATH, JUPYTER_WORK_PATH, WEB_CRAWL_PATH, NEBULA_PATH, CHROMA_PERSISTENT_PATH, LOCAL_LLM_MODEL_DIR, LOCAL_EM_MODEL_DIR]: if not os.path.exists(_path): os.makedirs(_path, exist_ok=True) diff --git a/configs/model_config.py.example b/configs/model_config.py.example index d4a0325..9cdf892 100644 --- a/configs/model_config.py.example +++ b/configs/model_config.py.example @@ -4,6 +4,7 @@ import logging import torch import openai import base64 +import json from .utils import is_running_in_docker from .default_config import * # 日志格式 @@ -29,26 +30,35 @@ try: client.visit_domain = os.environ.get("visit_domain") client.visit_biz = os.environ.get("visit_biz") client.visit_biz_line = os.environ.get("visit_biz_line") -except: +except Exception as e: + OPENAI_API_BASE = "https://api.openai.com/v1" + logger.error(e) pass + +try: + with open("./local_config.json", "r") as f: + update_config = json.load(f) +except: + update_config = {} + + # add your openai key -OPENAI_API_BASE = "https://api.openai.com/v1" -os.environ["API_BASE_URL"] = OPENAI_API_BASE -os.environ["OPENAI_API_KEY"] = "sk-xx" -openai.api_key = "sk-xx" +os.environ["API_BASE_URL"] = os.environ.get("API_BASE_URL") or update_config.get("API_BASE_URL") or OPENAI_API_BASE +os.environ["OPENAI_API_KEY"] = os.environ.get("OPENAI_API_KEY") or update_config.get("OPENAI_API_KEY") or "sk-xx" +openai.api_key = os.environ["OPENAI_API_KEY"] # os.environ["OPENAI_PROXY"] = "socks5h://127.0.0.1:13659" -os.environ["DUCKDUCKGO_PROXY"] = os.environ.get("DUCKDUCKGO_PROXY") or "socks5://127.0.0.1:13659" +os.environ["DUCKDUCKGO_PROXY"] = os.environ.get("DUCKDUCKGO_PROXY") or update_config.get("DUCKDUCKGO_PROXY") or "socks5h://127.0.0.1:13659" # ignore if you dont's use baidu_ocr_api os.environ["BAIDU_OCR_API_KEY"] = "xx" os.environ["BAIDU_OCR_SECRET_KEY"] = "xx" os.environ["log_verbose"] = "2" # LLM 名称 -EMBEDDING_ENGINE = 'model' # openai or model -EMBEDDING_MODEL = "text2vec-base" -LLM_MODEL = "gpt-3.5-turbo" -LLM_MODELs = ["gpt-3.5-turbo"] +EMBEDDING_ENGINE = os.environ.get("EMBEDDING_ENGINE") or update_config.get("EMBEDDING_ENGINE") or 'model' # openai or model +EMBEDDING_MODEL = os.environ.get("EMBEDDING_MODEL") or update_config.get("EMBEDDING_MODEL") or "text2vec-base" +LLM_MODEL = os.environ.get("LLM_MODEL") or "gpt-3.5-turbo" +LLM_MODELs = [LLM_MODEL] USE_FASTCHAT = "gpt" not in LLM_MODEL # 判断是否进行fastchat # LLM 运行设备 @@ -57,10 +67,12 @@ LLM_DEVICE = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mp # 在以下字典中修改属性值,以指定本地embedding模型存储位置 # 如将 "text2vec": "GanymedeNil/text2vec-large-chinese" 修改为 "text2vec": "User/Downloads/text2vec-large-chinese" # 此处请写绝对路径 -embedding_model_dict = { +embedding_model_dict = json.loads(os.environ.get("embedding_model_dict")) if os.environ.get("embedding_model_dict") else {} +embedding_model_dict = embedding_model_dict or update_config.get("EMBEDDING_MODEL") +embedding_model_dict = embedding_model_dict or { "ernie-tiny": "nghuyong/ernie-3.0-nano-zh", "ernie-base": "nghuyong/ernie-3.0-base-zh", - "text2vec-base": "shibing624/text2vec-base-chinese", + "text2vec-base": "text2vec-base-chinese", "text2vec": "GanymedeNil/text2vec-large-chinese", "text2vec-paraphrase": "shibing624/text2vec-base-chinese-paraphrase", "text2vec-sentence": "shibing624/text2vec-base-chinese-sentence", @@ -74,31 +86,35 @@ embedding_model_dict = { } -LOCAL_MODEL_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "embedding_models") -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()} +# LOCAL_MODEL_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "embedding_models") +# 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_DEVICE = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu" -ONLINE_LLM_MODEL = { +ONLINE_LLM_MODEL = json.loads(os.environ.get("ONLINE_LLM_MODEL")) if os.environ.get("ONLINE_LLM_MODEL") else {} +ONLINE_LLM_MODEL = ONLINE_LLM_MODEL or update_config.get("ONLINE_LLM_MODEL") +ONLINE_LLM_MODEL = ONLINE_LLM_MODEL or { # 线上模型。请在server_config中为每个在线API设置不同的端口 "openai-api": { "model_name": "gpt-3.5-turbo", - "api_base_url": "https://api.openai.com/v1", + "api_base_url": OPENAI_API_BASE, # "https://api.openai.com/v1", "api_key": "", "openai_proxy": "", }, "example": { - "version": "gpt-3.5", # 采用openai接口做示例 - "api_base_url": "https://api.openai.com/v1", + "version": "gpt-3.5-turbo", # 采用openai接口做示例 + "api_base_url": OPENAI_API_BASE, # "https://api.openai.com/v1", "api_key": "", "provider": "ExampleWorker", }, } # 建议使用chat模型,不要使用base,无法获取正确输出 -llm_model_dict = { +llm_model_dict = json.loads(os.environ.get("llm_model_dict")) if os.environ.get("llm_model_dict") else {} +llm_model_dict = llm_model_dict or update_config.get("llm_model_dict") +llm_model_dict = llm_model_dict or { "chatglm-6b": { "local_model_path": "THUDM/chatglm-6b", "api_base_url": "http://localhost:8888/v1", # "name"修改为fastchat服务中的"api_base_url" @@ -147,7 +163,9 @@ llm_model_dict = { } # 建议使用chat模型,不要使用base,无法获取正确输出 -VLLM_MODEL_DICT = { +VLLM_MODEL_DICT = json.loads(os.environ.get("VLLM_MODEL_DICT")) if os.environ.get("VLLM_MODEL_DICT") else {} +VLLM_MODEL_DICT = VLLM_MODEL_DICT or update_config.get("VLLM_MODEL_DICT") +VLLM_MODEL_DICT = VLLM_MODEL_DICT or { 'chatglm2-6b': "THUDM/chatglm-6b", } # 以下模型经过测试可接入,配置仿照上述即可 @@ -157,21 +175,21 @@ VLLM_MODEL_DICT = { # 'chatglm3-6b-base', 'Qwen-72B-Chat-Int4' -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 +# 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 -# -VLLM_MODEL_DICT_c = {} -for k, v in VLLM_MODEL_DICT.items(): - VLLM_MODEL_DICT_c[k] = f"/home/user/chatbot/llm_models/{v}" if is_running_in_docker() else f"{LOCAL_LLM_MODEL_DIR}/{v}" -VLLM_MODEL_DICT = VLLM_MODEL_DICT_c \ No newline at end of file +# llm_model_dict = llm_model_dict_c +# # +# VLLM_MODEL_DICT_c = {} +# for k, v in VLLM_MODEL_DICT.items(): +# VLLM_MODEL_DICT_c[k] = f"/home/user/chatbot/llm_models/{v}" if is_running_in_docker() else f"{LOCAL_LLM_MODEL_DIR}/{v}" +# VLLM_MODEL_DICT = VLLM_MODEL_DICT_c \ No newline at end of file diff --git a/configs/server_config.py.example b/configs/server_config.py.example index c313cf0..f22fa6b 100644 --- a/configs/server_config.py.example +++ b/configs/server_config.py.example @@ -1,13 +1,25 @@ from .model_config import LLM_MODEL, LLM_DEVICE -import os +import os, json + +try: + with open("./local_config.json", "r") as f: + update_config = json.load(f) +except: + update_config = {} # API 是否开启跨域,默认为False,如果需要开启,请设置为True # is open cross domain OPEN_CROSS_DOMAIN = False # 是否用容器来启动服务 -DOCKER_SERVICE = True +try: + DOCKER_SERVICE = json.loads(os.environ["DOCKER_SERVICE"]) or update_config.get("DOCKER_SERVICE") or False +except: + DOCKER_SERVICE = True # 是否采用容器沙箱 -SANDBOX_DO_REMOTE = True +try: + SANDBOX_DO_REMOTE = json.loads(os.environ["SANDBOX_DO_REMOTE"]) or update_config.get("SANDBOX_DO_REMOTE") or False +except: + SANDBOX_DO_REMOTE = True # 是否采用api服务来进行 NO_REMOTE_API = True # 各服务器默认绑定host @@ -61,7 +73,7 @@ NEBULA_GRAPH_SERVER = { # sandbox api server SANDBOX_CONTRAINER_NAME = "devopsgpt_sandbox" SANDBOX_IMAGE_NAME = "devopsgpt:py39" -SANDBOX_HOST = os.environ.get("SANDBOX_HOST") or DEFAULT_BIND_HOST # "172.25.0.3" +SANDBOX_HOST = os.environ.get("SANDBOX_HOST") or update_config.get("SANDBOX_HOST") or DEFAULT_BIND_HOST # "172.25.0.3" SANDBOX_SERVER = { "host": f"http://{SANDBOX_HOST}", "port": 5050, @@ -73,7 +85,10 @@ SANDBOX_SERVER = { # fastchat model_worker server # 这些模型必须是在model_config.llm_model_dict中正确配置的。 # 在启动startup.py时,可用通过`--model-worker --model-name xxxx`指定模型,不指定则为LLM_MODEL -FSCHAT_MODEL_WORKERS = { +# 建议使用chat模型,不要使用base,无法获取正确输出 +FSCHAT_MODEL_WORKERS = json.loads(os.environ.get("FSCHAT_MODEL_WORKERS")) if os.environ.get("FSCHAT_MODEL_WORKERS") else {} +FSCHAT_MODEL_WORKERS = FSCHAT_MODEL_WORKERS or update_config.get("FSCHAT_MODEL_WORKERS") +FSCHAT_MODEL_WORKERS = FSCHAT_MODEL_WORKERS or { "default": { "host": DEFAULT_BIND_HOST, "port": 20002, @@ -117,7 +132,9 @@ FSCHAT_MODEL_WORKERS = { 'chatglm3-6b-32k': {'host': DEFAULT_BIND_HOST, 'port': 20018}, 'chatglm3-6b-base': {'host': DEFAULT_BIND_HOST, 'port': 20019}, 'Qwen-72B-Chat-Int4': {'host': DEFAULT_BIND_HOST, 'port': 20020}, - 'gpt-3.5-turbo': {'host': DEFAULT_BIND_HOST, 'port': 20021} + 'gpt-3.5-turbo': {'host': DEFAULT_BIND_HOST, 'port': 20021}, + 'example': {'host': DEFAULT_BIND_HOST, 'port': 20022}, + 'openai-api': {'host': DEFAULT_BIND_HOST, 'port': 20023} } # fastchat multi model worker server FSCHAT_MULTI_MODEL_WORKERS = { diff --git a/examples/agent_examples/codeChatPhaseLocal_example.py b/examples/agent_examples/codeChatPhaseLocal_example.py index ff5ee24..172eebc 100644 --- a/examples/agent_examples/codeChatPhaseLocal_example.py +++ b/examples/agent_examples/codeChatPhaseLocal_example.py @@ -41,24 +41,16 @@ embed_config = EmbedConfig( # delete codebase -codebase_name = 'client_local' +codebase_name = 'client_nebula' code_path = '/Users/bingxu/Desktop/工作/大模型/chatbot/test_code_repo/client' code_path = "D://chromeDownloads/devopschat-bot/client_v2/client" use_nh = True -# cbh = CodeBaseHandler(codebase_name, code_path, crawl_type='dir', use_nh=use_nh, local_graph_path=CB_ROOT_PATH, -# llm_config=llm_config, embed_config=embed_config) +do_interpret = False cbh = CodeBaseHandler(codebase_name, code_path, crawl_type='dir', use_nh=use_nh, local_graph_path=CB_ROOT_PATH, llm_config=llm_config, embed_config=embed_config) cbh.delete_codebase(codebase_name=codebase_name) - # initialize codebase -codebase_name = 'client_local' -code_path = '/Users/bingxu/Desktop/工作/大模型/chatbot/test_code_repo/client' -code_path = "D://chromeDownloads/devopschat-bot/client_v2/client" -code_path = "/home/user/client" -use_nh = True -do_interpret = True cbh = CodeBaseHandler(codebase_name, code_path, crawl_type='dir', use_nh=use_nh, local_graph_path=CB_ROOT_PATH, llm_config=llm_config, embed_config=embed_config) cbh.import_code(do_interpret=do_interpret) @@ -78,25 +70,25 @@ phase = BasePhase( ## 需要启动容器中的nebula,采用use_nh=True来构建代码库,是可以通过cypher来查询 # round-1 -# query_content = "代码一共有多少类" -# query = Message( -# role_name="human", role_type="user", -# role_content=query_content, input_query=query_content, origin_query=query_content, -# code_engine_name="client_1", score_threshold=1.0, top_k=3, cb_search_type="cypher" -# ) -# -# output_message1, _ = phase.step(query) -# print(output_message1) +query_content = "代码一共有多少类" +query = Message( + role_name="human", role_type="user", + role_content=query_content, input_query=query_content, origin_query=query_content, + code_engine_name="client_1", score_threshold=1.0, top_k=3, cb_search_type="cypher" + ) + +output_message1, _ = phase.step(query) +print(output_message1) # round-2 -# query_content = "代码库里有哪些函数,返回5个就行" -# query = Message( -# role_name="human", role_type="user", -# role_content=query_content, input_query=query_content, origin_query=query_content, -# code_engine_name="client_1", score_threshold=1.0, top_k=3, cb_search_type="cypher" -# ) -# output_message2, _ = phase.step(query) -# print(output_message2) +query_content = "代码库里有哪些函数,返回5个就行" +query = Message( + role_name="human", role_type="user", + role_content=query_content, input_query=query_content, origin_query=query_content, + code_engine_name="client_1", score_threshold=1.0, top_k=3, cb_search_type="cypher" + ) +output_message2, _ = phase.step(query) +print(output_message2) # round-3 diff --git a/examples/agent_examples/searchChatPhase_example.py b/examples/agent_examples/searchChatPhase_example.py index cecafb7..4049694 100644 --- a/examples/agent_examples/searchChatPhase_example.py +++ b/examples/agent_examples/searchChatPhase_example.py @@ -7,7 +7,6 @@ sys.path.append(src_dir) from configs.model_config import KB_ROOT_PATH, JUPYTER_WORK_PATH from configs.server_config import SANDBOX_SERVER -from coagent.tools import toLangchainTools, TOOL_DICT, TOOL_SETS from coagent.llm_models.llm_config import EmbedConfig, LLMConfig from coagent.connector.phase import BasePhase diff --git a/examples/model_workers/__init__.py b/examples/model_workers/__init__.py index b0e5941..65d2bb6 100644 --- a/examples/model_workers/__init__.py +++ b/examples/model_workers/__init__.py @@ -16,3 +16,12 @@ from .baichuan import BaiChuanWorker from .azure import AzureWorker from .tiangong import TianGongWorker from .openai import ExampleWorker + + +IMPORT_MODEL_WORKERS = [ + ChatGLMWorker, MiniMaxWorker, XingHuoWorker, QianFanWorker, FangZhouWorker, + QwenWorker, BaiChuanWorker, AzureWorker, TianGongWorker, ExampleWorker +] + +MODEL_WORKER_SETS = [tool.__name__ for tool in IMPORT_MODEL_WORKERS] + diff --git a/examples/model_workers/base.py b/examples/model_workers/base.py index 88bc51f..9d03149 100644 --- a/examples/model_workers/base.py +++ b/examples/model_workers/base.py @@ -1,6 +1,5 @@ from fastchat.conversation import Conversation -from configs.model_config import LOG_PATH -# from coagent.base_configs.env_config import LOG_PATH +from configs.default_config import LOG_PATH import fastchat.constants fastchat.constants.LOGDIR = LOG_PATH from fastchat.serve.base_model_worker import BaseModelWorker diff --git a/examples/start.py b/examples/start.py index 2275cfb..b0e16d6 100644 --- a/examples/start.py +++ b/examples/start.py @@ -1,4 +1,4 @@ -import docker, sys, os, time, requests, psutil +import docker, sys, os, time, requests, psutil, json import subprocess from docker.types import Mount, DeviceRequest from loguru import logger @@ -25,9 +25,6 @@ def check_process(content: str, lang: str = None, do_stop=False): '''process-not-exist is true, process-exist is false''' 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(): if content in str(process.info["cmdline"]).lower(): logger.info(f"content, {process.info}") # 关闭进程 @@ -106,7 +103,7 @@ def start_sandbox_service(network_name ='my_network'): ) mounts = [mount] # 沙盒的启动与服务的启动是独立的 - if SANDBOX_SERVER["do_remote"]: + if SANDBOX_DO_REMOTE: client = docker.from_env() networks = client.networks.list() if any([network_name==i.attrs["Name"] for i in networks]): @@ -159,18 +156,6 @@ def start_api_service(sandbox_host=DEFAULT_BIND_HOST): target='/home/user/chatbot/', read_only=False # 如果需要只读访问,将此选项设置为True ) - # mount_database = Mount( - # type='bind', - # source=os.path.join(src_dir, "knowledge_base"), - # target='/home/user/knowledge_base/', - # read_only=False # 如果需要只读访问,将此选项设置为True - # ) - # mount_code_database = Mount( - # type='bind', - # source=os.path.join(src_dir, "code_base"), - # target='/home/user/code_base/', - # read_only=False # 如果需要只读访问,将此选项设置为True - # ) ports={ f"{API_SERVER['docker_port']}/tcp": f"{API_SERVER['port']}/tcp", f"{WEBUI_SERVER['docker_port']}/tcp": f"{WEBUI_SERVER['port']}/tcp", @@ -208,6 +193,8 @@ def start_api_service(sandbox_host=DEFAULT_BIND_HOST): if check_docker(client, CONTRAINER_NAME, do_stop=True): container = start_docker(client, script_shs, ports, IMAGE_NAME, CONTRAINER_NAME, mounts, network=network_name) + logger.info("You can open http://localhost:8501 to use chatbot!") + else: logger.info("start local service") # 关闭之前启动的docker 服务 @@ -234,12 +221,17 @@ def start_api_service(sandbox_host=DEFAULT_BIND_HOST): subprocess.Popen(webui_sh, shell=True) + logger.info("You can please open http://localhost:8501 to use chatbot!") -if __name__ == "__main__": +def start_main(): + global SANDBOX_DO_REMOTE, DOCKER_SERVICE + SANDBOX_DO_REMOTE = SANDBOX_DO_REMOTE if os.environ.get("SANDBOX_DO_REMOTE") is None else json.loads(os.environ.get("SANDBOX_DO_REMOTE")) + DOCKER_SERVICE = DOCKER_SERVICE if os.environ.get("DOCKER_SERVICE") is None else json.loads(os.environ.get("DOCKER_SERVICE")) + start_sandbox_service() sandbox_host = DEFAULT_BIND_HOST - if SANDBOX_SERVER["do_remote"]: + if SANDBOX_DO_REMOTE: client = docker.from_env() containers = client.containers.list(all=True) @@ -252,3 +244,5 @@ if __name__ == "__main__": start_api_service(sandbox_host) +if __name__ == "__main__": + start_main() diff --git a/examples/start.sh b/examples/start.sh new file mode 100644 index 0000000..ac52ee3 --- /dev/null +++ b/examples/start.sh @@ -0,0 +1,7 @@ +#!/bin/bash + + +cp ../configs/model_config.py.example ../configs/model_config.py +cp ../configs/server_config.py.example ../configs/server_config.py + +streamlit run webui_config.py --server.port 8510 diff --git a/examples/stop.py b/examples/stop.py index 0c8f3ea..2ed3dac 100644 --- a/examples/stop.py +++ b/examples/stop.py @@ -17,14 +17,20 @@ try: except: client = None -# -check_docker(client, SANDBOX_CONTRAINER_NAME, do_stop=True, ) -check_process(f"port={SANDBOX_SERVER['port']}", do_stop=True) -check_process(f"port=5050", do_stop=True) -# -check_docker(client, CONTRAINER_NAME, do_stop=True, ) -check_process("api.py", do_stop=True) -check_process("sdfile_api.py", do_stop=True) -check_process("llm_api.py", do_stop=True) -check_process("webui.py", do_stop=True) +def stop_main(): + # + check_docker(client, SANDBOX_CONTRAINER_NAME, do_stop=True, ) + check_process(f"port={SANDBOX_SERVER['port']}", do_stop=True) + check_process(f"port=5050", do_stop=True) + + # + check_docker(client, CONTRAINER_NAME, do_stop=True, ) + check_process("api.py", do_stop=True) + check_process("sdfile_api.py", do_stop=True) + check_process("llm_api.py", do_stop=True) + check_process("webui.py", do_stop=True) + + +if __name__ == "__main__": + stop_main() \ No newline at end of file diff --git a/examples/webui/document.py b/examples/webui/document.py index 104cb36..ad5b2a9 100644 --- a/examples/webui/document.py +++ b/examples/webui/document.py @@ -357,7 +357,7 @@ def knowledge_page( empty.progress(0.0, "") for d in api.recreate_vector_store( kb, vs_type=default_vs_type, embed_model=embedding_model, embedding_device=EMBEDDING_DEVICE, - embed_model_path=embedding_model_dict[EMBEDDING_MODEL], embed_engine=EMBEDDING_ENGINE, + embed_model_path=embedding_model_dict[embedding_model], embed_engine=EMBEDDING_ENGINE, api_key=llm_model_dict[LLM_MODEL]["api_key"], api_base_url=llm_model_dict[LLM_MODEL]["api_base_url"], ): diff --git a/examples/webui_config.py b/examples/webui_config.py new file mode 100644 index 0000000..08af1d5 --- /dev/null +++ b/examples/webui_config.py @@ -0,0 +1,208 @@ +import streamlit as st +import docker +import torch, os, sys, json +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.default_config import * + +import platform +system_name = platform.system() + + +VERSION = "v0.1.0" + +MODEL_WORKER_SETS = [ + "ChatGLMWorker", "MiniMaxWorker", "XingHuoWorker", "QianFanWorker", "FangZhouWorker", + "QwenWorker", "BaiChuanWorker", "AzureWorker", "TianGongWorker", "ExampleWorker" +] + +openai_models = ["gpt-3.5-turbo", "gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k", "gpt-4"] +embedding_models = ["openai"] + + +st.write("启动配置页面!") + +st.write("如果你要使用语言模型,请将LLM放到 ~/Codefuse-chatbot/llm_models") + +st.write("如果你要使用向量模型,请将向量模型放到 ~/Codefuse-chatbot/embedding_models") + +with st.container(): + + col1, col2 = st.columns(2) + with col1.container(): + llm_model_name = st.selectbox('LLM Model Name', openai_models + [i for i in os.listdir(LOCAL_LLM_MODEL_DIR) if os.path.isdir(os.path.join(LOCAL_LLM_MODEL_DIR, i))]) + + llm_apikey = st.text_input('填写 LLM API KEY', 'EMPTY') + llm_apiurl = st.text_input('填写 LLM API URL', 'http://localhost:8888/v1') + + llm_engine = st.selectbox('选择哪个llm引擎', ["online", "fastchat", "fastchat-vllm"]) + llm_model_port = st.text_input('LLM Model Port,非fastchat模式可无视', '20006') + llm_provider_option = st.selectbox('选择哪个online模型加载器,非online可无视', ["openai"] + MODEL_WORKER_SETS) + + if llm_engine == "online" and llm_provider_option == "openai": + try: + from zdatafront import OPENAI_API_BASE + except: + OPENAI_API_BASE = "https://api.openai.com/v1" + llm_apiurl = OPENAI_API_BASE + + device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu" + + FSCHAT_MODEL_WORKERS = { + llm_model_name: { + 'host': "127.0.0.1", 'port': llm_model_port, + "device": device, + # todo: 多卡加载需要配置的参数 + "gpus": None, + "numgpus": 1,}, + } + + + ONLINE_LLM_MODEL, llm_model_dict, VLLM_MODEL_DICT = {}, {}, {} + if llm_engine == "online": + ONLINE_LLM_MODEL = { + llm_model_name: { + "model_name": llm_model_name, + "version": llm_model_name, + "api_base_url": llm_apiurl, # "https://api.openai.com/v1", + "api_key": llm_apikey, + "openai_proxy": "", + "provider": llm_provider_option + }, + } + + if llm_engine == "fastchat": + llm_model_dict = { + llm_model_name: { + "local_model_path": llm_model_name, + "api_base_url": llm_apiurl, # "name"修改为fastchat服务中的"api_base_url" + "api_key": llm_apikey + }} + + + if llm_engine == "fastchat-vllm": + VLLM_MODEL_DICT = { + llm_model_name: { + "local_model_path": llm_model_name, + "api_base_url": llm_apiurl, # "name"修改为fastchat服务中的"api_base_url" + "api_key": llm_apikey + } + } + llm_model_dict = { + llm_model_name: { + "local_model_path": llm_model_name, + "api_base_url": llm_apiurl, # "name"修改为fastchat服务中的"api_base_url" + "api_key": llm_apikey + }} + + + with col2.container(): + em_model_name = st.selectbox('Embedding Model Name', [i for i in os.listdir(LOCAL_EM_MODEL_DIR) if os.path.isdir(os.path.join(LOCAL_EM_MODEL_DIR, i))] + embedding_models) + em_engine = st.selectbox('选择哪个embedding引擎', ["model", "openai"]) + device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu" + embedding_model_dict = {em_model_name: em_model_name} + # em_apikey = st.text_input('Embedding API KEY', '') + # em_apiurl = st.text_input('Embedding API URL', '') + +# +try: + client = docker.from_env() + has_docker = True +except: + has_docker = False + +if has_docker: + with st.container(): + DOCKER_SERVICE = st.toggle('DOCKER_SERVICE', True) + SANDBOX_DO_REMOTE = st.toggle('SANDBOX_DO_REMOTE', True) +else: + DOCKER_SERVICE = False + SANDBOX_DO_REMOTE = False + + +with st.container(): + cols = st.columns(3) + + if cols[0].button( + "重启服务,按前配置生效", + use_container_width=True, + ): + from start import start_main + from stop import stop_main + stop_main() + start_main() + if cols[1].button( + "停止服务", + use_container_width=True, + ): + from stop import stop_main + stop_main() + + if cols[2].button( + "启动对话服务", + use_container_width=True + ): + + os.environ["API_BASE_URL"] = llm_apiurl + os.environ["OPENAI_API_KEY"] = llm_apikey + + os.environ["EMBEDDING_ENGINE"] = em_engine + os.environ["EMBEDDING_MODEL"] = em_model_name + os.environ["LLM_MODEL"] = llm_model_name + + embedding_model_dict = {k: f"/home/user/chatbot/embedding_models/{v}" if DOCKER_SERVICE else f"{LOCAL_EM_MODEL_DIR}/{v}" for k, v in embedding_model_dict.items()} + os.environ["embedding_model_dict"] = json.dumps(embedding_model_dict) + + os.environ["ONLINE_LLM_MODEL"] = json.dumps(ONLINE_LLM_MODEL) + + # 模型路径重置 + 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 DOCKER_SERVICE 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 + os.environ["llm_model_dict"] = json.dumps(llm_model_dict) + # + VLLM_MODEL_DICT_c = {} + for k, v in VLLM_MODEL_DICT.items(): + VLLM_MODEL_DICT_c[k] = f"/home/user/chatbot/llm_models/{v}" if DOCKER_SERVICE else f"{LOCAL_LLM_MODEL_DIR}/{v}" + VLLM_MODEL_DICT = VLLM_MODEL_DICT_c + os.environ["VLLM_MODEL_DICT"] = json.dumps(VLLM_MODEL_DICT) + + # server config + os.environ["DOCKER_SERVICE"] = json.dumps(DOCKER_SERVICE) + os.environ["SANDBOX_DO_REMOTE"] = json.dumps(SANDBOX_DO_REMOTE) + os.environ["FSCHAT_MODEL_WORKERS"] = json.dumps(FSCHAT_MODEL_WORKERS) + + update_json = { + "API_BASE_URL": llm_apiurl, + "OPENAI_API_KEY": llm_apikey, + "EMBEDDING_ENGINE": em_engine, + "EMBEDDING_MODEL": em_model_name, + "LLM_MODEL": llm_model_name, + "embedding_model_dict": json.dumps(embedding_model_dict), + "llm_model_dict": json.dumps(llm_model_dict), + "ONLINE_LLM_MODEL": json.dumps(ONLINE_LLM_MODEL), + "VLLM_MODEL_DICT": json.dumps(VLLM_MODEL_DICT), + "DOCKER_SERVICE": json.dumps(DOCKER_SERVICE), + "SANDBOX_DO_REMOTE": json.dumps(SANDBOX_DO_REMOTE), + "FSCHAT_MODEL_WORKERS": json.dumps(FSCHAT_MODEL_WORKERS) + } + + with open(os.path.join(src_dir, "configs/local_config.json"), "w") as f: + json.dump(update_json, f) + + from start import start_main + from stop import stop_main + stop_main() + start_main() diff --git a/sources/docs_imgs/webui_config.png b/sources/docs_imgs/webui_config.png new file mode 100644 index 0000000000000000000000000000000000000000..711b5f7fba296df69e82c65170b1635a96f4cedd GIT binary patch literal 56204 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≦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