From 5de3b6e1623e8c551d874b4b7a65e976c91329bd Mon Sep 17 00:00:00 2001 From: Hongyu Shi Date: Mon, 18 Aug 2025 14:17:51 +0800 Subject: [PATCH 01/12] =?UTF-8?q?fix(build):=20=E6=B7=BB=E5=8A=A0=20PyInst?= =?UTF-8?q?aller=20=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=EF=BC=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=8F=AF=E6=89=A7=E8=A1=8C=E6=96=87=E4=BB=B6=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E8=BF=87=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hongyu Shi --- distribution/linux/euler-copilot-shell.spec | 11 ++- oi-cli.spec | 80 +++++++++++++++++++++ 2 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 oi-cli.spec diff --git a/distribution/linux/euler-copilot-shell.spec b/distribution/linux/euler-copilot-shell.spec index 2667921..83bab27 100644 --- a/distribution/linux/euler-copilot-shell.spec +++ b/distribution/linux/euler-copilot-shell.spec @@ -57,12 +57,11 @@ pip install -r requirements.txt # 安装PyInstaller pip install pyinstaller -# 使用虚拟环境中的PyInstaller创建单一可执行文件 -pyinstaller --noconfirm --onefile \ - --name %{pypi_name} \ - --add-data "src/app/css:app/css" \ - --target-architecture %{_target_cpu} \ - src/main.py +# 使用虚拟环境中的 PyInstaller 创建单一可执行文件 +# 使用专用的 .spec 文件解决 Textual 动态导入问题 +pyinstaller --noconfirm \ + --distpath dist \ + oi-cli.spec # 退出虚拟环境 deactivate diff --git a/oi-cli.spec b/oi-cli.spec new file mode 100644 index 0000000..955e7c8 --- /dev/null +++ b/oi-cli.spec @@ -0,0 +1,80 @@ +"""PyInstaller 配置文件""" + +import os +from pathlib import Path + +# 项目根目录 +project_root = Path(os.getcwd()) +src_dir = project_root / "src" + +# 隐藏导入 +hidden_imports = [ + # Textual widgets 相关模块 + "textual.widgets._tab_pane", + "textual.widgets._tabbed_content", + "textual.widgets._button", + "textual.widgets._input", + "textual.widgets._label", + "textual.widgets._progress_bar", + "textual.widgets._rich_log", + "textual.widgets._static", + "textual.widgets._header", + "textual.widgets._tree", + "textual.widgets._data_table", + "textual.widgets._option_list", + "textual.widgets._footer", + "textual.widgets._loading_indicator", + "textual.widgets._text_area", + "textual.widgets._markdown_viewer", + "textual.widgets._select", + "textual.widgets._checkbox", + "textual.widgets._radio_button", + "textual.widgets._sparkline", + "textual.widgets._switch", + "textual.widgets._tabs", +] + +# 数据文件 +added_files = [ + (str(src_dir / "app" / "css" / "styles.tcss"), "app/css"), +] + +a = Analysis( + [str(src_dir / "main.py")], + pathex=[str(src_dir)], + binaries=[], + datas=added_files, + hiddenimports=hidden_imports, + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=None, + noarchive=False, +) + +pyz = PYZ(a.pure, a.zipped_data, cipher=None) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name="oi-cli", + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) -- Gitee From 55c1dccc7d1f95e63125ad2444e81ef18b499c64 Mon Sep 17 00:00:00 2001 From: Hongyu Shi Date: Mon, 18 Aug 2025 10:48:53 +0800 Subject: [PATCH 02/12] =?UTF-8?q?fix(client):=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E5=8F=82=E6=95=B0=E4=B8=BA=E7=AE=80=E5=8C=96?= =?UTF-8?q?=E7=9A=84=E8=AF=AD=E8=A8=80=E4=BB=A3=E7=A0=81=20"zh"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hongyu Shi --- src/backend/hermes/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/hermes/client.py b/src/backend/hermes/client.py index f1b4fc0..be25919 100644 --- a/src/backend/hermes/client.py +++ b/src/backend/hermes/client.py @@ -155,7 +155,7 @@ class HermesChatClient(LLMClientBase): conversation_id=conversation_id, question=prompt, features=HermesFeatures(), - language="zh_cn", + language="zh", ) # 直接传递异常,不在这里处理 -- Gitee From 4e9d4e68d27c752c7f8418b052fac188af86acc5 Mon Sep 17 00:00:00 2001 From: Hongyu Shi Date: Mon, 18 Aug 2025 14:35:03 +0800 Subject: [PATCH 03/12] =?UTF-8?q?fix(deploy-ui):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=8C=89=E9=92=AE=E6=A0=B7=E5=BC=8F=EF=BC=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E9=BB=98=E8=AE=A4=E5=9C=B0=E5=9D=80=E4=B8=BA?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hongyu Shi --- src/app/deployment/ui.py | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/src/app/deployment/ui.py b/src/app/deployment/ui.py index 0f335c4..76a3746 100644 --- a/src/app/deployment/ui.py +++ b/src/app/deployment/ui.py @@ -80,14 +80,8 @@ class EnvironmentCheckScreen(ModalScreen[bool]): align: center middle; } - .continue-button { + .continue-button, .exit-button { margin: 0 1; - background: $success; - } - - .exit-button { - margin: 0 1; - background: $error; } """ @@ -112,8 +106,8 @@ class EnvironmentCheckScreen(ModalScreen[bool]): yield Static("检查管理员权限...", id="sudo_desc", classes="check-description") with Horizontal(classes="button-row"): - yield Button("继续配置", id="continue", classes="continue-button", disabled=True) - yield Button("退出", id="exit", classes="exit-button") + yield Button("继续配置", id="continue", variant="success", classes="continue-button", disabled=True) + yield Button("退出", id="exit", variant="error", classes="exit-button") async def on_mount(self) -> None: """界面挂载时开始环境检查""" @@ -252,12 +246,6 @@ class EnvironmentErrorScreen(ModalScreen[None]): border: solid $secondary; padding: 1; } - - .ok-button { - margin: 1 0; - align: center middle; - background: $primary; - } """ def __init__(self, title: str, messages: list[str]) -> None: @@ -286,7 +274,7 @@ class EnvironmentErrorScreen(ModalScreen[None]): yield Static("") yield Static("请解决上述问题后重新运行部署助手。") - yield Button("确定退出", id="ok", classes="ok-button") + yield Button("确定退出", id="ok", variant="primary") @on(Button.Pressed, "#ok") def on_ok_button_pressed(self) -> None: @@ -402,6 +390,7 @@ class DeploymentConfigScreen(ModalScreen[bool]): with Horizontal(classes="form-row"): yield Label("服务器 IP 地址:", classes="form-label") yield Input( + value="127.0.0.1", # 默认为本地地址 placeholder="例如:127.0.0.1", id="server_ip", classes="form-input", @@ -1002,12 +991,6 @@ class ErrorMessageScreen(ModalScreen[None]): margin: 1 0; max-height: 20; } - - .ok-button { - margin: 1 0; - align: center middle; - background: $primary; - } """ def __init__(self, title: str, messages: list[str]) -> None: @@ -1032,7 +1015,7 @@ class ErrorMessageScreen(ModalScreen[None]): for message in self.messages: yield Static(f"• {message}") - yield Button("确定", id="ok", classes="ok-button") + yield Button("确定", id="ok", variant="primary") @on(Button.Pressed, "#ok") def on_ok_button_pressed(self) -> None: -- Gitee From 5f1e84815e0755f91149a91a55a263862010eeed Mon Sep 17 00:00:00 2001 From: Hongyu Shi Date: Mon, 18 Aug 2025 15:09:52 +0800 Subject: [PATCH 04/12] =?UTF-8?q?fix(deploy):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E9=83=A8=E7=BD=B2=E8=BF=9B=E7=A8=8B=E7=9A=84=E5=BC=82=E6=AD=A5?= =?UTF-8?q?=E5=A4=84=E7=90=86=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=BF=83=E8=B7=B3?= =?UTF-8?q?=E6=9C=BA=E5=88=B6=E4=BB=A5=E9=98=B2=E6=AD=A2=E7=95=8C=E9=9D=A2?= =?UTF-8?q?=E5=8D=A1=E6=AD=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hongyu Shi --- src/app/deployment/service.py | 66 ++++++++++++++++++++++++++++++----- src/app/deployment/ui.py | 41 +++++++++++++++++++--- 2 files changed, 94 insertions(+), 13 deletions(-) diff --git a/src/app/deployment/service.py b/src/app/deployment/service.py index c754356..342d89b 100644 --- a/src/app/deployment/service.py +++ b/src/app/deployment/service.py @@ -7,6 +7,7 @@ from __future__ import annotations import asyncio +import contextlib import platform import re from pathlib import Path @@ -358,7 +359,17 @@ class DeploymentService: output_lines = [] if process.stdout: while True: - line = await process.stdout.readline() + try: + # 使用超时读取,避免长时间阻塞 + line = await asyncio.wait_for( + process.stdout.readline(), + timeout=0.1, # 100ms 超时 + ) + except TimeoutError: + # 超时时让出控制权给 UI 事件循环 + await asyncio.sleep(0) + continue + if not line: break @@ -369,6 +380,9 @@ class DeploymentService: temp_state.add_log(f"安装: {decoded_line}") progress_callback(temp_state) + # 每次读取后让出控制权 + await asyncio.sleep(0) + # 等待进程结束 return_code = await process.wait() return return_code == 0, output_lines @@ -497,14 +511,24 @@ class DeploymentService: cwd=script_dir, ) - # 读取输出 - async for line in self._read_process_output(): - self.state.add_log(line) - if progress_callback: - progress_callback(self.state) + # 创建心跳任务,定期更新界面 + heartbeat_task = asyncio.create_task(self._heartbeat_progress(progress_callback)) + + try: + # 读取输出 + async for line in self._read_process_output(): + self.state.add_log(line) + if progress_callback: + progress_callback(self.state) + + # 等待进程结束 + return_code = await self._process.wait() + finally: + # 取消心跳任务 + heartbeat_task.cancel() + with contextlib.suppress(asyncio.CancelledError): + await heartbeat_task - # 等待进程结束 - return_code = await self._process.wait() self._process = None if return_code == 0: @@ -520,6 +544,17 @@ class DeploymentService: self.state.add_log(f"✗ {script_name}执行失败,返回码: {return_code}") return False + async def _heartbeat_progress(self, progress_callback: Callable[[DeploymentState], None] | None) -> None: + """心跳进度更新,确保界面不会卡死""" + if not progress_callback: + return + + with contextlib.suppress(asyncio.CancelledError): + while True: + await asyncio.sleep(1.0) # 每秒更新一次 + if progress_callback: + progress_callback(self.state) + async def _generate_config_files( self, config: DeploymentConfig, @@ -667,7 +702,17 @@ class DeploymentService: while True: try: - line = await self._process.stdout.readline() + # 使用超时读取,避免长时间阻塞 + try: + line = await asyncio.wait_for( + self._process.stdout.readline(), + timeout=0.1, # 100ms 超时 + ) + except TimeoutError: + # 超时时让出控制权给 UI 事件循环 + await asyncio.sleep(0) + continue + if not line: break @@ -675,6 +720,9 @@ class DeploymentService: if decoded_line: yield decoded_line + # 每次读取后让出控制权 + await asyncio.sleep(0) + except OSError as e: logger.warning("读取进程输出时发生错误: %s", e) break diff --git a/src/app/deployment/ui.py b/src/app/deployment/ui.py index 76a3746..00d5ea4 100644 --- a/src/app/deployment/ui.py +++ b/src/app/deployment/ui.py @@ -7,6 +7,7 @@ from __future__ import annotations import asyncio +import contextlib from typing import TYPE_CHECKING from textual import on @@ -842,6 +843,10 @@ class DeploymentProgressScreen(ModalScreen[bool]): self.query_one("#step_label", Static).update("部署已取消") self.query_one("#deployment_log", RichLog).write("部署已被用户取消") + # 等待任务真正结束 + with contextlib.suppress(asyncio.CancelledError): + await self.deployment_task + # 更新按钮状态 self._update_buttons_after_failure() else: @@ -850,6 +855,11 @@ class DeploymentProgressScreen(ModalScreen[bool]): def _reset_ui_for_retry(self) -> None: """重置界面用于重试""" + # 取消之前的任务 + if self.deployment_task and not self.deployment_task.done(): + self.deployment_task.cancel() + self.deployment_task = None + # 清空日志 log_widget = self.query_one("#deployment_log", RichLog) log_widget.clear() @@ -885,13 +895,36 @@ class DeploymentProgressScreen(ModalScreen[bool]): async def _start_deployment(self) -> None: """开始部署流程""" try: + # 创建异步任务但不等待,让它在后台运行 self.deployment_task = asyncio.create_task(self._execute_deployment()) - await self.deployment_task - except asyncio.CancelledError: - self.query_one("#step_label", Static).update("部署已取消") - self.query_one("#deployment_log", RichLog).write("部署被取消") + + # 启动一个定时器来检查任务状态 + self.set_interval(0.1, self._check_deployment_status) + + except (OSError, RuntimeError) as e: + self.query_one("#step_label", Static).update("部署启动失败") + self.query_one("#deployment_log", RichLog).write(f"部署启动失败: {e}") self._update_buttons_after_failure() + def _check_deployment_status(self) -> None: + """检查部署任务状态""" + if self.deployment_task is None: + return + + if self.deployment_task.done(): + # 任务完成,停止定时器 + try: + # 获取任务结果,如果有异常会在这里抛出 + self.deployment_task.result() + except asyncio.CancelledError: + self.query_one("#step_label", Static).update("部署已取消") + self.query_one("#deployment_log", RichLog).write("部署被取消") + self._update_buttons_after_failure() + except (OSError, RuntimeError, ValueError) as e: + self.query_one("#step_label", Static).update("部署异常") + self.query_one("#deployment_log", RichLog).write(f"部署异常: {e}") + self._update_buttons_after_failure() + async def _execute_deployment(self) -> None: """执行部署过程""" try: -- Gitee From 8ce7cfa4a2e8622bed30c847afa9b747b5f3ee6a Mon Sep 17 00:00:00 2001 From: Hongyu Shi Date: Mon, 18 Aug 2025 15:39:51 +0800 Subject: [PATCH 05/12] =?UTF-8?q?fix(deploy):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E6=9B=B4=E6=96=B0=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=AE=89=E5=85=A8=E6=9B=BF?= =?UTF-8?q?=E6=8D=A2=E6=96=B9=E6=B3=95=E5=B9=B6=E5=A4=84=E7=90=86=E5=A4=87?= =?UTF-8?q?=E4=BB=BD=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hongyu Shi --- src/app/deployment/service.py | 113 ++++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 27 deletions(-) diff --git a/src/app/deployment/service.py b/src/app/deployment/service.py index 342d89b..7cd7f9d 100644 --- a/src/app/deployment/service.py +++ b/src/app/deployment/service.py @@ -62,38 +62,62 @@ class DeploymentResourceManager: @classmethod def update_config_values(cls, content: str, config: DeploymentConfig) -> str: """根据用户配置更新配置文件内容""" + + def safe_replace(pattern: str, replacement: str, text: str) -> str: + """安全的正则表达式替换,避免反向引用问题""" + # 使用 lambda 函数来避免反向引用问题 + return re.sub(pattern, lambda m: m.group(1) + replacement, text) + # 更新 LLM 配置 - content = re.sub(r"(MODEL_NAME\s*=\s*).*", rf"\1{config.llm.model}", content) - content = re.sub(r"(OPENAI_API_BASE\s*=\s*).*", rf"\1{config.llm.endpoint}", content) - content = re.sub(r"(OPENAI_API_KEY\s*=\s*).*", rf"\1{config.llm.api_key}", content) - content = re.sub(r"(MAX_TOKENS\s*=\s*).*", rf"\1{config.llm.max_tokens}", content) - content = re.sub(r"(TEMPERATURE\s*=\s*).*", rf"\1{config.llm.temperature}", content) - content = re.sub(r"(REQUEST_TIMEOUT\s*=\s*).*", rf"\1{config.llm.request_timeout}", content) + content = safe_replace(r"(MODEL_NAME\s*=\s*).*", config.llm.model, content) + content = safe_replace(r"(OPENAI_API_BASE\s*=\s*).*", config.llm.endpoint, content) + content = safe_replace(r"(OPENAI_API_KEY\s*=\s*).*", config.llm.api_key, content) + content = safe_replace(r"(MAX_TOKENS\s*=\s*).*", str(config.llm.max_tokens), content) + content = safe_replace(r"(TEMPERATURE\s*=\s*).*", str(config.llm.temperature), content) + content = safe_replace(r"(REQUEST_TIMEOUT\s*=\s*).*", str(config.llm.request_timeout), content) # 更新 Embedding 配置 - content = re.sub(r"(EMBEDDING_TYPE\s*=\s*).*", rf"\1{config.embedding.type}", content) - content = re.sub(r"(EMBEDDING_API_KEY\s*=\s*).*", rf"\1{config.embedding.api_key}", content) - content = re.sub(r"(EMBEDDING_ENDPOINT\s*=\s*).*", rf"\1{config.embedding.endpoint}", content) - return re.sub(r"(EMBEDDING_MODEL_NAME\s*=\s*).*", rf"\1{config.embedding.model}", content) + content = safe_replace(r"(EMBEDDING_TYPE\s*=\s*).*", config.embedding.type, content) + content = safe_replace(r"(EMBEDDING_API_KEY\s*=\s*).*", config.embedding.api_key, content) + content = safe_replace(r"(EMBEDDING_ENDPOINT\s*=\s*).*", config.embedding.endpoint, content) + return safe_replace(r"(EMBEDDING_MODEL_NAME\s*=\s*).*", config.embedding.model, content) @classmethod def update_toml_values(cls, content: str, config: DeploymentConfig) -> str: """更新 TOML 配置文件的值""" + + def safe_replace_quoted(pattern: str, replacement: str, text: str) -> str: + """安全替换引号内的值""" + return re.sub(pattern, lambda m: m.group(1) + replacement + m.group(2), text) + + def safe_replace_number(pattern: str, replacement: str, text: str) -> str: + """安全替换数字值""" + return re.sub(pattern, lambda m: m.group(1) + replacement, text) + # 更新服务器 IP - content = re.sub(r"(host\s*=\s*')[^']*(')", rf"\1http://{config.server_ip}:8000\2", content) - content = re.sub(r"(login_api\s*=\s*')[^']*(')", rf"\1http://{config.server_ip}:8080/api/auth/login\2", content) - content = re.sub(r"(domain\s*=\s*')[^']*(')", rf"\1{config.server_ip}\2", content) + server_ip = str(config.server_ip) + content = safe_replace_quoted( + r"(host\s*=\s*')[^']*(')", + f"http://{server_ip}:8000", + content, + ) + content = safe_replace_quoted( + r"(login_api\s*=\s*')[^']*(')", + f"http://{server_ip}:8080/api/auth/login", + content, + ) + content = safe_replace_quoted(r"(domain\s*=\s*')[^']*(')", server_ip, content) # 更新 LLM 配置 - content = re.sub(r'(endpoint\s*=\s*")[^"]*(")', rf"\1{config.llm.endpoint}\2", content) - content = re.sub(r"(key\s*=\s*')[^']*(')", rf"\1{config.llm.api_key}\2", content) - content = re.sub(r"(model\s*=\s*')[^']*(')", rf"\1{config.llm.model}\2", content) - content = re.sub(r"(max_tokens\s*=\s*)\d+", rf"\1{config.llm.max_tokens}", content) - content = re.sub(r"(temperature\s*=\s*)[\d.]+", rf"\1{config.llm.temperature}", content) + content = safe_replace_quoted(r'(endpoint\s*=\s*")[^"]*(")', config.llm.endpoint, content) + content = safe_replace_quoted(r"(key\s*=\s')[^']*(')", config.llm.api_key, content) + content = safe_replace_quoted(r"(model\s*=\s')[^']*(')", config.llm.model, content) + content = safe_replace_number(r"(max_tokens\s*=\s*)\d+", str(config.llm.max_tokens), content) + content = safe_replace_number(r"(temperature\s*=\s*)[\d.]+", str(config.llm.temperature), content) # 更新 Embedding 配置 - content = re.sub(r"(type\s*=\s*')[^']*(')", rf"\1{config.embedding.type}\2", content) - return re.sub(r"(api_key\s*=\s*')[^']*(')", rf"\1{config.embedding.api_key}\2", content) + content = safe_replace_quoted(r"(type\s*=\s*')[^']*(')", config.embedding.type, content) + return safe_replace_quoted(r"(api_key\s*=\s*')[^']*(')", config.embedding.api_key, content) @classmethod def create_deploy_mode_content(cls, config: DeploymentConfig) -> str: @@ -651,7 +675,17 @@ class DeploymentService: str(self.resource_manager.ENV_TEMPLATE), f"{self.resource_manager.ENV_TEMPLATE}.backup", ] - await asyncio.create_subprocess_exec(*backup_cmd) + backup_process = await asyncio.create_subprocess_exec( + *backup_cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + _, backup_stderr = await backup_process.communicate() + + if backup_process.returncode != 0: + error_msg = backup_stderr.decode("utf-8", errors="ignore").strip() + msg = f"备份 env 文件失败: {error_msg}" + raise RuntimeError(msg) # 写入更新后的内容 write_cmd = ["sudo", "tee", str(self.resource_manager.ENV_TEMPLATE)] @@ -662,7 +696,12 @@ class DeploymentService: stderr=asyncio.subprocess.PIPE, ) - await process.communicate(updated_content.encode()) + _, write_stderr = await process.communicate(updated_content.encode()) + + if process.returncode != 0: + error_msg = write_stderr.decode("utf-8", errors="ignore").strip() + msg = f"写入 env 文件失败: {error_msg}" + raise RuntimeError(msg) async def _update_config_toml(self, config: DeploymentConfig) -> None: """更新 config.toml 配置文件""" @@ -682,7 +721,17 @@ class DeploymentService: str(self.resource_manager.CONFIG_TEMPLATE), f"{self.resource_manager.CONFIG_TEMPLATE}.backup", ] - await asyncio.create_subprocess_exec(*backup_cmd) + backup_process = await asyncio.create_subprocess_exec( + *backup_cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + _, backup_stderr = await backup_process.communicate() + + if backup_process.returncode != 0: + error_msg = backup_stderr.decode("utf-8", errors="ignore").strip() + msg = f"备份 config.toml 文件失败: {error_msg}" + raise RuntimeError(msg) # 写入更新后的内容 write_cmd = ["sudo", "tee", str(self.resource_manager.CONFIG_TEMPLATE)] @@ -693,7 +742,12 @@ class DeploymentService: stderr=asyncio.subprocess.PIPE, ) - await process.communicate(updated_content.encode()) + _, write_stderr = await process.communicate(updated_content.encode()) + + if process.returncode != 0: + error_msg = write_stderr.decode("utf-8", errors="ignore").strip() + msg = f"写入 config.toml 文件失败: {error_msg}" + raise RuntimeError(msg) async def _read_process_output(self) -> AsyncGenerator[str, None]: """读取进程输出""" @@ -758,7 +812,10 @@ class DeploymentService: try: return await self._execute_agent_init_command( - agent_script_path, config_file_path, temp_state, progress_callback, + agent_script_path, + config_file_path, + temp_state, + progress_callback, ) except Exception as e: temp_state.add_log(f"❌ Agent 初始化异常: {e!s}") @@ -779,8 +836,10 @@ class DeploymentService: cmd = [ "python3", str(agent_script_path), - "--operator", "comb", - "--config_path", str(config_file_path), + "--operator", + "comb", + "--config_path", + str(config_file_path), ] temp_state.add_log(f"执行命令: {' '.join(cmd)}") -- Gitee From 580f1f7d8bfe47d803ba714010f5feb00e27f92f Mon Sep 17 00:00:00 2001 From: Hongyu Shi Date: Mon, 18 Aug 2025 15:56:13 +0800 Subject: [PATCH 06/12] =?UTF-8?q?refactor(deploy):=20=E7=A7=BB=E9=99=A4=20?= =?UTF-8?q?MinIO=20=E7=9B=B8=E5=85=B3=E5=AE=89=E8=A3=85=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hongyu Shi --- scripts/deploy/1-check-env/check_env.sh | 2 - .../install_openEulerIntelligence.sh | 107 ++++-------- .../deploy/3-install-server/init_config.sh | 156 ------------------ scripts/deploy/deploy.sh | 22 ++- 4 files changed, 38 insertions(+), 249 deletions(-) diff --git a/scripts/deploy/1-check-env/check_env.sh b/scripts/deploy/1-check-env/check_env.sh index e42d3e7..abd8bbf 100644 --- a/scripts/deploy/1-check-env/check_env.sh +++ b/scripts/deploy/1-check-env/check_env.sh @@ -53,13 +53,11 @@ install_wget() { # 基础URL列表(无论RAG是否启用都需要检测) base_urls_x86=( - "https://dl.min.io/server/minio/release/linux-amd64/archive/minio-20250524170830.0.0-1.x86_64.rpm" "https://downloads.mongodb.com/compass/mongodb-mongosh-2.5.2.x86_64.rpm" "https://repo.mongodb.org/yum/redhat/9/mongodb-org/7.0/x86_64/RPMS/mongodb-org-server-7.0.21-1.el9.x86_64.rpm" ) base_urls_arm=( - "https://dl.min.io/server/minio/release/linux-arm64/minio" "https://repo.mongodb.org/yum/redhat/9/mongodb-org/7.0/aarch64/RPMS/mongodb-org-server-7.0.21-1.el9.aarch64.rpm" "https://downloads.mongodb.com/compass/mongodb-mongosh-2.5.2.aarch64.rpm" ) diff --git a/scripts/deploy/2-install-dependency/install_openEulerIntelligence.sh b/scripts/deploy/2-install-dependency/install_openEulerIntelligence.sh index 94a6a1a..3aea318 100644 --- a/scripts/deploy/2-install-dependency/install_openEulerIntelligence.sh +++ b/scripts/deploy/2-install-dependency/install_openEulerIntelligence.sh @@ -284,67 +284,17 @@ install_zhparser() { return 0 } is_x86_architecture() { - # 获取系统架构信息(使用 uname -m 或 arch 命令) - local arch - arch=$(uname -m) # 多数系统支持,返回架构名称(如 x86_64、i686、aarch64 等) - # 备选:arch 命令,输出与 uname -m 类似 - # arch=$(arch) - - # x86 架构的常见标识:i386、i686(32位),x86_64(64位) - if [[ $arch == i386 || $arch == i686 || $arch == x86_64 ]]; then - return 0 # 是 x86 架构,返回 0(成功) - else - return 1 # 非 x86 架构,返回 1(失败) - fi -} -# 安装MinIO -install_minio() { - echo -e "${COLOR_INFO}[Info] 开始安装MinIO...${COLOR_RESET}" - local minio_dir="/opt/minio" - if ! mkdir -p "$minio_dir"; then - echo -e "${COLOR_ERROR}[Error] 创建目录失败: $minio_dir${COLOR_RESET}" - return 1 - fi - ! is_x86_architecture || { - local minio_url="https://dl.min.io/server/minio/release/linux-amd64/archive/minio-20250524170830.0.0-1.x86_64.rpm" - local minio_src="../5-resource/rpm/minio-20250524170830.0.0-1.x86_64.rpm" - local minio_file="/opt/minio/minio-20250524170830.0.0-1.x86_64.rpm" - - if [ -f "$minio_src" ]; then - cp -r "$minio_src" "$minio_file" - sleep 1 - fi - if [ ! -f "$minio_file" ]; then - echo -e "${COLOR_INFO}[Info] 正在下载MinIO软件包...${COLOR_RESET}" - if ! wget "$minio_url" --no-check-certificate -O "$minio_file"; then - echo -e "${COLOR_ERROR}[Error] MinIO下载失败${COLOR_RESET}" - return 1 - fi - fi - - dnf install -y $minio_file || { - echo -e "${COLOR_ERROR}[Error] MinIO安装失败${COLOR_RESET}" - return 1 - } - echo -e "${COLOR_SUCCESS}[Success] MinIO安装成功...${COLOR_RESET}" - return 0 - } - echo -e "${COLOR_INFO}[Info] 下载MinIO二进制文件(aarch64)...${COLOR_RESET}" - local minio_url="https://dl.min.io/server/minio/release/linux-arm64/minio" - local temp_dir=$minio_dir - local minio_path="../5-resource/rpm/minio" - - # 检查文件是否已存在 - if [ -f "$minio_path" ]; then - cp -r $minio_path $temp_dir - echo -e "${COLOR_INFO}[Info] MinIO二进制文件已存在,跳过下载${COLOR_RESET}" + # 获取系统架构信息(使用 uname -m 或 arch 命令) + local arch + arch=$(uname -m) # 多数系统支持,返回架构名称(如 x86_64、i686、aarch64 等) + # 备选:arch 命令,输出与 uname -m 类似 + # arch=$(arch) + + # x86 架构的常见标识:i386、i686(32位),x86_64(64位) + if [[ $arch == i386 || $arch == i686 || $arch == x86_64 ]]; then + return 0 # 是 x86 架构,返回 0(成功) else - echo -e "${COLOR_INFO}[Info] 下载MinIO二进制文件(aarch64)...${COLOR_RESET}" - if ! wget -q --show-progress "$minio_url" -O "$temp_dir/minio" --no-check-certificate; then - echo -e "${COLOR_ERROR}[Error] 下载MinIO失败,请检查网络连接${COLOR_RESET}" - rm -rf "$temp_dir" - return 1 - fi + return 1 # 非 x86 架构,返回 1(失败) fi } @@ -519,16 +469,16 @@ check_pip() { return 0 } -install_framework(){ +install_framework() { echo -e "\n${COLOR_INFO}[Info] 开始安装框架服务...${COLOR_RESET}" local pkgs=( - "euler-copilot-framework" - "git" - "make" - "gcc" - "gcc-c++" - "tar" - "python3-pip" + "euler-copilot-framework" + "git" + "make" + "gcc" + "gcc-c++" + "tar" + "python3-pip" ) if ! install_and_verify "${pkgs[@]}"; then echo -e "${COLOR_ERROR}[Error] dnf安装验证未通过!${COLOR_RESET}" @@ -539,16 +489,16 @@ install_framework(){ install_mongodb || return 1 check_pip || return 1 } -install_rag(){ +install_rag() { local pkgs=( - "euler-copilot-rag" - "clang" - "llvm" - "java-17-openjdk" - "postgresql" - "postgresql-server" - "postgresql-server-devel" - "libpq-devel" + "euler-copilot-rag" + "clang" + "llvm" + "java-17-openjdk" + "postgresql" + "postgresql-server" + "postgresql-server-devel" + "libpq-devel" ) if ! install_and_verify "${pkgs[@]}"; then echo -e "${COLOR_ERROR}[Error] dnf安装验证未通过!${COLOR_RESET}" @@ -560,9 +510,8 @@ install_rag(){ install_pgvector || return 1 cd "$SCRIPT_DIR" || return 1 install_zhparser || return 1 - install_minio || return 1 } -install_web(){ +install_web() { local pkgs=( "nginx" "redis" diff --git a/scripts/deploy/3-install-server/init_config.sh b/scripts/deploy/3-install-server/init_config.sh index 27d561a..961a991 100644 --- a/scripts/deploy/3-install-server/init_config.sh +++ b/scripts/deploy/3-install-server/init_config.sh @@ -399,162 +399,7 @@ is_x86_architecture() { return 1 # 非 x86 架构,返回 1(失败) fi } -# 安装配置MinIO(非x86架构专用) -install_minio_arrch64() { - # 仅在非x86架构执行(主要针对aarch64) - if is_x86_architecture; then - echo -e "${COLOR_INFO}[Info] 当前为x86架构,跳过MinIO安装流程${COLOR_RESET}" - return 0 - fi - - # 检查是否已设置密码,未设置则使用默认值 - if [ -z "$MINIO_ROOT_PASSWORD" ]; then - echo -e "${COLOR_INFO}[Info] 未指定MINIO_ROOT_PASSWORD,使用默认密码${COLOR_RESET}" - MINIO_ROOT_PASSWORD="minioadmin" # 默认为minioadmin - fi - - echo -e "${COLOR_INFO}[Info] 开始在非x86架构上安装配置MinIO...${COLOR_RESET}" - - # 1. 检查系统架构(主要支持aarch64) - local arch=$(uname -m) - if [ "$arch" != "aarch64" ]; then - echo -e "${COLOR_WARN}[Warn] 检测到未知架构: $arch,仅推荐在aarch64上使用此脚本${COLOR_RESET}" - fi - - # 2. 下载对应架构的MinIO二进制文件 - echo -e "${COLOR_INFO}[Info] 下载MinIO二进制文件(aarch64)...${COLOR_RESET}" - local minio_url="https://dl.min.io/server/minio/release/linux-arm64/minio" - local temp_dir=$(mktemp -d) - - if ! wget -q --show-progress "$minio_url" -O "$temp_dir/minio"; then - echo -e "${COLOR_ERROR}[Error] 下载MinIO失败,请检查网络连接${COLOR_RESET}" - rm -rf "$temp_dir" - return 1 - fi - - # 3. 安装二进制文件 - echo -e "${COLOR_INFO}[Info] 安装MinIO到系统目录...${COLOR_RESET}" - chmod +x "$temp_dir/minio" - mv "$temp_dir/minio" /usr/local/bin/ || { - echo -e "${COLOR_ERROR}[Error] 无法安装MinIO到/usr/local/bin${COLOR_RESET}" - rm -rf "$temp_dir" - return 1 - } - rm -rf "$temp_dir" - - # 验证二进制文件 - if ! file /usr/local/bin/minio | grep -q "ARM aarch64"; then - echo -e "${COLOR_ERROR}[Error] MinIO二进制文件架构不匹配${COLOR_RESET}" - rm -f /usr/local/bin/minio - return 1 - fi - - # 4. 配置MinIO环境变量 - echo -e "${COLOR_INFO}[Info] 配置MinIO环境变量...${COLOR_RESET}" - cat >/etc/default/minio </dev/null; then - groupadd minio-user - useradd -g minio-user --shell=/sbin/nologin -r minio-user || { - echo -e "${COLOR_ERROR}[Error] 创建minio-user失败${COLOR_RESET}" - return 1 - } - fi - - mkdir -p /var/lib/minio - chown -R minio-user:minio-user /var/lib/minio || { - echo -e "${COLOR_ERROR}[Error] 无法设置/var/lib/minio权限${COLOR_RESET}" - return 1 - } - - # 6. 配置systemd服务 - echo -e "${COLOR_INFO}[Info] 配置MinIO系统服务...${COLOR_RESET}" - cat >/etc/systemd/system/minio.service <<'EOF' -[Unit] -Description=MinIO Object Storage Server -Documentation=https://min.io/docs/minio/linux/index.html -Wants=network-online.target -After=network-online.target - -[Service] -User=minio-user -Group=minio-user -EnvironmentFile=/etc/default/minio -ExecStart=/usr/local/bin/minio server $MINIO_VOLUMES -Restart=always -RestartSec=5 -LimitNOFILE=65536 - -[Install] -WantedBy=multi-user.target -EOF - # 7. 启动并启用服务 - echo -e "${COLOR_INFO}[Info] 启动MinIO服务...${COLOR_RESET}" - systemctl daemon-reload - systemctl enable --now minio || { - echo -e "${COLOR_ERROR}[Error] MinIO服务启动失败${COLOR_RESET}" - return 1 - } - - # 8. 检查服务状态 - echo -e "${COLOR_INFO}[Info] 验证MinIO服务状态...${COLOR_RESET}" - if systemctl is-active --quiet minio; then - echo -e "${COLOR_SUCCESS}[Success] MinIO安装配置完成" - return 0 - else - echo -e "${COLOR_ERROR}[Error] MinIO服务未正常运行,请查看日志: journalctl -u minio -f${COLOR_RESET}" - return 1 - fi -} - -# 安装配置MinIO -install_minio() { - ! is_x86_architecture || { - echo -e "${COLOR_INFO}[Info] 开始配置MinIO...${COLOR_RESET}" - - cat >/etc/default/minio </dev/null; then - groupadd minio-user - useradd -g minio-user --shell=/sbin/nologin -r minio-user - fi - - mkdir -p /var/lib/minio - chown -R minio-user:minio-user /var/lib/minio - - # 5. 启动服务 - echo -e "${COLOR_INFO}[Info] 启动MinIO服务...${COLOR_RESET}" - systemctl enable --now minio || { - echo -e "${COLOR_ERROR}[Error] MinIO服务启动失败${COLOR_RESET}" - return 1 - } - - # 6. 检查服务状态 - echo -e "${COLOR_INFO}[Info] 检查MinIO服务状态...${COLOR_RESET}" - if ! systemctl is-active --quiet minio; then - echo -e "${COLOR_ERROR}[Error] MinIO服务未正常运行${COLOR_RESET}" - return 1 - fi - - echo -e "${COLOR_SUCCESS}[Success] MinIO配置完成${COLOR_RESET}" - return 0 - } - install_minio_arrch64 || return 1 - return 0 - -} install_rag() { echo -e "${COLOR_INFO}[Info] 开始初始化配置 euler-copilot-rag...${COLOR_RESET}" @@ -920,7 +765,6 @@ main() { # 切换到脚本所在目录 cd "$SCRIPT_DIR" || return 1 update_password - install_minio || return 1 install_components || return 1 install_framework || return 1 cd "$SCRIPT_DIR" || return 1 diff --git a/scripts/deploy/deploy.sh b/scripts/deploy/deploy.sh index 3d08b50..bce7f15 100644 --- a/scripts/deploy/deploy.sh +++ b/scripts/deploy/deploy.sh @@ -59,12 +59,11 @@ show_restart_menu() { echo "2) framework" echo "3) rag" echo "4) mysql" - echo "5) minio" - echo "6) redis" - echo "7) postgresql" - echo "8) 返回主菜单" + echo "5) redis" + echo "6) postgresql" + echo "7) 返回主菜单" echo "==============================" - echo -n "请输入要重启的服务编号(1-8): " + echo -n "请输入要重启的服务编号(1-7): " } # 询问用户并保存安装模式 ask_install_options() { @@ -215,7 +214,7 @@ check_existing_install_model() { # 手动部署子菜单循环 manual_deployment_loop() { while true; do -# show_sub_menu + # show_sub_menu show_sub_model_menu read -r sub_choice run_sub_model_script "$sub_choice" @@ -272,7 +271,7 @@ show_help() { echo "" echo -e "${BLUE}选项:${COLOR_RESET}" echo " 无参数 进入交互式菜单" -# echo " --q 切换安装部署模式" + # echo " --q 切换安装部署模式" echo " --h 显示本帮助信息" echo " --help 同 --h" echo " --a 进入agent初始化模式,详见部署文档" @@ -313,7 +312,7 @@ fi # return 0 #fi if [[ "$1" == "--a" ]]; then - agent_manager "${@:2}" + agent_manager "${@:2}" exit 0 fi # 获取主脚本绝对路径并切换到所在目录 @@ -347,10 +346,9 @@ while true; do 2) service="framework" ;; 3) service="rag" ;; 4) service="mysqld" ;; - 5) service="minio" ;; - 6) service="redis" ;; - 7) service="postgresql" ;; - 8) break ;; + 5) service="redis" ;; + 6) service="postgresql" ;; + 7) break ;; *) echo -e "${COLOR_ERROR}无效的选项,请输入1-8之间的数字${COLOR_RESET}" continue -- Gitee From ea360c23faf63f8d1ea1a16e14c88707041ceb26 Mon Sep 17 00:00:00 2001 From: Hongyu Shi Date: Mon, 18 Aug 2025 16:50:22 +0800 Subject: [PATCH 07/12] =?UTF-8?q?feat(deploy):=20=E5=BC=95=E5=85=A5=20toml?= =?UTF-8?q?=20=E5=BA=93=E6=AD=A3=E7=A1=AE=E7=BC=96=E8=BE=91=20toml=20?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hongyu Shi --- requirements.txt | 1 + setup.py | 1 + src/app/deployment/service.py | 82 +++++++++++++++++++++-------------- 3 files changed, 51 insertions(+), 33 deletions(-) diff --git a/requirements.txt b/requirements.txt index c6545ce..ae91114 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ openai==1.99.6 rich==14.1.0 ruff==0.12.8 textual==5.3.0 +toml==0.10.2 diff --git a/setup.py b/setup.py index 5f858f7..0fd4721 100644 --- a/setup.py +++ b/setup.py @@ -21,6 +21,7 @@ setup( "openai==1.99.6", "rich==14.1.0", "textual==5.3.0", + "toml==0.10.2", ], entry_points={ "console_scripts": [ diff --git a/src/app/deployment/service.py b/src/app/deployment/service.py index 7cd7f9d..16a229a 100644 --- a/src/app/deployment/service.py +++ b/src/app/deployment/service.py @@ -13,6 +13,8 @@ import re from pathlib import Path from typing import TYPE_CHECKING +import toml + from log.manager import get_logger from .models import DeploymentConfig, DeploymentState @@ -85,39 +87,53 @@ class DeploymentResourceManager: @classmethod def update_toml_values(cls, content: str, config: DeploymentConfig) -> str: """更新 TOML 配置文件的值""" - - def safe_replace_quoted(pattern: str, replacement: str, text: str) -> str: - """安全替换引号内的值""" - return re.sub(pattern, lambda m: m.group(1) + replacement + m.group(2), text) - - def safe_replace_number(pattern: str, replacement: str, text: str) -> str: - """安全替换数字值""" - return re.sub(pattern, lambda m: m.group(1) + replacement, text) - - # 更新服务器 IP - server_ip = str(config.server_ip) - content = safe_replace_quoted( - r"(host\s*=\s*')[^']*(')", - f"http://{server_ip}:8000", - content, - ) - content = safe_replace_quoted( - r"(login_api\s*=\s*')[^']*(')", - f"http://{server_ip}:8080/api/auth/login", - content, - ) - content = safe_replace_quoted(r"(domain\s*=\s*')[^']*(')", server_ip, content) - - # 更新 LLM 配置 - content = safe_replace_quoted(r'(endpoint\s*=\s*")[^"]*(")', config.llm.endpoint, content) - content = safe_replace_quoted(r"(key\s*=\s')[^']*(')", config.llm.api_key, content) - content = safe_replace_quoted(r"(model\s*=\s')[^']*(')", config.llm.model, content) - content = safe_replace_number(r"(max_tokens\s*=\s*)\d+", str(config.llm.max_tokens), content) - content = safe_replace_number(r"(temperature\s*=\s*)[\d.]+", str(config.llm.temperature), content) - - # 更新 Embedding 配置 - content = safe_replace_quoted(r"(type\s*=\s*')[^']*(')", config.embedding.type, content) - return safe_replace_quoted(r"(api_key\s*=\s*')[^']*(')", config.embedding.api_key, content) + try: + # 解析 TOML 内容 + toml_data = toml.loads(content) + + # 更新服务器 IP + server_ip = str(config.server_ip) + if "login" in toml_data and "settings" in toml_data["login"]: + toml_data["login"]["settings"]["host"] = f"http://{server_ip}:8000" + toml_data["login"]["settings"]["login_api"] = f"http://{server_ip}:8080/api/auth/login" + + # 更新 fastapi 域名 + if "fastapi" in toml_data: + toml_data["fastapi"]["domain"] = server_ip + + # 更新 LLM 配置 + if "llm" in toml_data: + toml_data["llm"]["endpoint"] = config.llm.endpoint + toml_data["llm"]["key"] = config.llm.api_key + toml_data["llm"]["model"] = config.llm.model + toml_data["llm"]["max_tokens"] = config.llm.max_tokens + toml_data["llm"]["temperature"] = config.llm.temperature + + # 更新 function_call 配置 + if "function_call" in toml_data: + toml_data["function_call"]["backend"] = "function_call" + toml_data["function_call"]["endpoint"] = config.llm.endpoint + toml_data["function_call"]["api_key"] = config.llm.api_key + toml_data["function_call"]["model"] = config.llm.model + toml_data["function_call"]["max_tokens"] = config.llm.max_tokens + toml_data["function_call"]["temperature"] = config.llm.temperature + + # 更新 Embedding 配置 + if "embedding" in toml_data: + toml_data["embedding"]["type"] = config.embedding.type + toml_data["embedding"]["api_key"] = config.embedding.api_key + + # 将更新后的数据转换回 TOML 格式 + return toml.dumps(toml_data) + + except toml.TomlDecodeError as e: + logger.exception("解析 TOML 内容时出错") + msg = f"TOML 格式错误: {e}" + raise ValueError(msg) from e + except Exception as e: + logger.exception("更新 TOML 配置时发生错误") + msg = f"更新 TOML 配置失败: {e}" + raise RuntimeError(msg) from e @classmethod def create_deploy_mode_content(cls, config: DeploymentConfig) -> str: -- Gitee From 4a25f4c8301e3cfe85dc5311665e9fb5da0b3990 Mon Sep 17 00:00:00 2001 From: Hongyu Shi Date: Mon, 18 Aug 2025 17:14:13 +0800 Subject: [PATCH 08/12] chore: clean code Signed-off-by: Hongyu Shi --- .../deploy/4-other-script/agent_manager.py | 93 ++++++++----------- 1 file changed, 40 insertions(+), 53 deletions(-) diff --git a/scripts/deploy/4-other-script/agent_manager.py b/scripts/deploy/4-other-script/agent_manager.py index a038b55..fc20690 100644 --- a/scripts/deploy/4-other-script/agent_manager.py +++ b/scripts/deploy/4-other-script/agent_manager.py @@ -1,22 +1,22 @@ +import argparse +import asyncio import json -import sys +import logging import os import shutil -import argparse -import logging -from typing import Optional, Dict, List, Any +import sys from enum import Enum -from pydantic import BaseModel, Field, ValidationError +from typing import Any, Dict, List, Optional import aiohttp -import asyncio from aiohttp import ClientError, ClientResponseError +from pydantic import BaseModel, Field, ValidationError # 配置日志系统 logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", - handlers=[logging.StreamHandler(sys.stdout)] + handlers=[logging.StreamHandler(sys.stdout)], ) logger = logging.getLogger("mcp_manager") @@ -31,18 +31,21 @@ SERVICE_WAIT_TIMEOUT = 60 # 服务等待超时时间(秒) class AppType(str, Enum): """应用中心应用类型""" + FLOW = "flow" AGENT = "agent" class AppLink(BaseModel): """App的相关链接""" + title: str = Field(description="链接标题") url: str = Field(..., description="链接地址", pattern=r"^(https|http)://.*$") class PermissionType(str, Enum): """权限类型""" + PROTECTED = "protected" PUBLIC = "public" PRIVATE = "private" @@ -50,6 +53,7 @@ class PermissionType(str, Enum): class AppPermissionData(BaseModel): """应用权限数据结构""" + type: PermissionType = Field( default=PermissionType.PRIVATE, alias="visibility", @@ -64,6 +68,7 @@ class AppPermissionData(BaseModel): class AppFlowInfo(BaseModel): """应用工作流数据结构""" + id: str = Field(..., description="工作流ID") name: str = Field(..., description="工作流名称") description: str = Field(..., description="工作流简介") @@ -72,16 +77,17 @@ class AppFlowInfo(BaseModel): class AppData(BaseModel): """应用信息数据结构""" + app_type: AppType = Field(..., alias="appType", description="应用类型") icon: str = Field(default="", description="图标") name: str = Field(..., max_length=20, description="应用名称") description: str = Field(..., max_length=150, description="应用简介") links: List[AppLink] = Field(default=[], description="相关链接", max_length=5) - first_questions: List[str] = Field( - default=[], alias="recommendedQuestions", description="推荐问题", max_length=3) + first_questions: List[str] = Field(default=[], alias="recommendedQuestions", description="推荐问题", max_length=3) history_len: int = Field(3, alias="dialogRounds", ge=1, le=10, description="对话轮次(1~10)") permission: AppPermissionData = Field( - default_factory=lambda: AppPermissionData(authorizedUsers=None), description="权限配置") + default_factory=lambda: AppPermissionData(authorizedUsers=None), description="权限配置" + ) workflows: List[AppFlowInfo] = Field(default=[], description="工作流信息列表") mcp_service: List[str] = Field(default=[], alias="mcpService", description="MCP服务id列表") @@ -115,16 +121,12 @@ class ApiClient: url = f"{self.base_url}{path}" for retry in range(MAX_RETRY_COUNT): try: - async with self.session.request( - method, url, timeout=REQUEST_TIMEOUT, **kwargs - ) as response: + async with self.session.request(method, url, timeout=REQUEST_TIMEOUT, **kwargs) as response: response.raise_for_status() return await response.json() except (ClientResponseError, ClientError) as e: - logger.warning( - f"API请求失败(第{retry + 1}/{MAX_RETRY_COUNT}次) - {method} {url}: {str(e)}" - ) + logger.warning(f"API请求失败(第{retry + 1}/{MAX_RETRY_COUNT}次) - {method} {url}: {str(e)}") if retry < MAX_RETRY_COUNT - 1: await asyncio.sleep(RETRY_DELAY) @@ -186,7 +188,7 @@ def get_config(config_path: str) -> dict: raise FileNotFoundError(f"配置文件不存在: {config_path}") try: - with open(config_path, 'r', encoding='utf-8') as reader: + with open(config_path, "r", encoding="utf-8") as reader: config = json.load(reader) if not isinstance(config, dict): @@ -264,10 +266,10 @@ async def process_mcp_config(api_client: ApiClient, config_path: str) -> str: config = get_config(config_path) # 先删除已存在的服务 - if 'serviceId' in config: + if "serviceId" in config: try: - await delete_mcp_server(api_client, config['serviceId']) - del config['serviceId'] + await delete_mcp_server(api_client, config["serviceId"]) + del config["serviceId"] logger.info("已删除旧的MCP服务ID") except Exception as e: logger.warning(f"删除旧MCP服务失败(可能不存在),继续创建新服务: {str(e)}") @@ -277,8 +279,8 @@ async def process_mcp_config(api_client: ApiClient, config_path: str) -> str: # 保存更新后的配置文件 try: - config['serviceId'] = server_id - with open(config_path, 'w', encoding='utf-8') as writer: + config["serviceId"] = server_id + with open(config_path, "w", encoding="utf-8") as writer: json.dump(config, writer, ensure_ascii=False, indent=4) logger.info(f"配置文件已更新: {config_path}") @@ -317,7 +319,7 @@ async def install_mcp_server(api_client: ApiClient, mcp_id: str) -> Dict[str, An for service in services: if service.get("mcpserviceId") == mcp_id: logger.debug(f"MCP服务 {mcp_id} 状态: {service.get('status')}") - if service.get('status') != "ready": + if service.get("status") != "ready": logger.debug(f"开始安装MCP服务{mcp_id}") return await api_client.request("POST", f"/api/mcp/{mcp_id}/install") break @@ -357,7 +359,6 @@ async def call_app_api(api_client: ApiClient, appdata: AppData) -> str: async def get_app_list(api_client: ApiClient) -> str: """查询智能体应用agent list""" try: - response = await api_client.request("GET", "/api/app") return str(response) @@ -410,8 +411,8 @@ async def create_agent(api_client: ApiClient, config_path: str) -> None: await activate_mcp_server(api_client, service_id) # 创建应用数据 - app_name = mcp_server.get('name', f"agent_{service_id[:6]}")[:20] - app_desc = mcp_server.get('description', f"Auto-created agent for {service_id}")[:150] + app_name = mcp_server.get("name", f"agent_{service_id[:6]}")[:20] + app_desc = mcp_server.get("description", f"Auto-created agent for {service_id}")[:150] app_data = AppData( appType=AppType.AGENT, @@ -420,10 +421,7 @@ async def create_agent(api_client: ApiClient, config_path: str) -> None: icon="", mcpService=[service_id], name=app_name, - permission=AppPermissionData( - visibility=PermissionType.PUBLIC, - authorizedUsers=[] - ) + permission=AppPermissionData(visibility=PermissionType.PUBLIC, authorizedUsers=[]), ) # 创建并发布应用 @@ -433,26 +431,15 @@ async def create_agent(api_client: ApiClient, config_path: str) -> None: async def main_async(): - parser = argparse.ArgumentParser(description='MCP服务器管理工具') - parser.add_argument( - 'operator', - choices=['init', 'create', 'comb'], - help='操作指令:init(初始化mcp server)、create(创建agent)、comb(创建组合mcp的agent)' - ) - parser.add_argument( - 'config_path', - help='MCP配置文件的路径(例如:/opt/mcp-servers/config.json)要求是全路径' - ) - parser.add_argument( - '-v', '--verbose', - action='store_true', - help='显示详细日志信息' - ) + parser = argparse.ArgumentParser(description="MCP服务器管理工具") parser.add_argument( - '--url', - help=f'MCP服务基础URL,默认: {BASE_URL}', - default=BASE_URL + "operator", + choices=["init", "create", "comb"], + help="操作指令:init(初始化mcp server)、create(创建agent)、comb(创建组合mcp的agent)", ) + parser.add_argument("config_path", help="MCP配置文件的路径(例如:/opt/mcp-servers/config.json)要求是全路径") + parser.add_argument("-v", "--verbose", action="store_true", help="显示详细日志信息") + parser.add_argument("--url", help=f"MCP服务基础URL,默认: {BASE_URL}", default=BASE_URL) args = parser.parse_args() @@ -469,11 +456,11 @@ async def main_async(): api_client = ApiClient(args.url) try: - if args.operator == 'init': + if args.operator == "init": await process_mcp_config(api_client, args.config_path) - elif args.operator == 'create': + elif args.operator == "create": await create_agent(api_client, args.config_path) - elif args.operator == 'comb': + elif args.operator == "comb": await comb_create(api_client, args.config_path) logger.info("操作执行成功") except Exception as e: @@ -492,5 +479,5 @@ def main(): sys.exit(1) -if __name__ == '__main__': - main() \ No newline at end of file +if __name__ == "__main__": + main() -- Gitee From 747d4776573eb5877626a08ccf8320b67a09c771 Mon Sep 17 00:00:00 2001 From: Hongyu Shi Date: Mon, 18 Aug 2025 17:14:28 +0800 Subject: [PATCH 09/12] =?UTF-8?q?fix(deploy):=20=E4=BF=AE=E6=AD=A3=20Agent?= =?UTF-8?q?=20=E5=88=9D=E5=A7=8B=E5=8C=96=E5=91=BD=E4=BB=A4=E6=9E=84?= =?UTF-8?q?=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hongyu Shi --- src/app/deployment/service.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/deployment/service.py b/src/app/deployment/service.py index 16a229a..008d3f1 100644 --- a/src/app/deployment/service.py +++ b/src/app/deployment/service.py @@ -849,12 +849,11 @@ class DeploymentService: ) -> bool: """执行 Agent 初始化命令""" # 构建 Agent 初始化命令(默认执行 comb 操作) + # 根据 agent_manager.py 的参数定义:operator 和 config_path 是位置参数 cmd = [ "python3", str(agent_script_path), - "--operator", "comb", - "--config_path", str(config_file_path), ] -- Gitee From 47d29b21eeaba92f7b7c273076469800bb8640ee Mon Sep 17 00:00:00 2001 From: Hongyu Shi Date: Mon, 18 Aug 2025 17:35:40 +0800 Subject: [PATCH 10/12] =?UTF-8?q?fix(deploy):=20=E5=9C=A8=20Agent=20?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E5=91=BD=E4=BB=A4=E4=B8=AD=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20sudo=20=E6=9D=83=E9=99=90=E4=BB=A5=E8=AE=BF?= =?UTF-8?q?=E9=97=AE=E7=B3=BB=E7=BB=9F=E7=BA=A7=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hongyu Shi --- src/app/deployment/service.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/deployment/service.py b/src/app/deployment/service.py index 008d3f1..a4736bb 100644 --- a/src/app/deployment/service.py +++ b/src/app/deployment/service.py @@ -850,7 +850,9 @@ class DeploymentService: """执行 Agent 初始化命令""" # 构建 Agent 初始化命令(默认执行 comb 操作) # 根据 agent_manager.py 的参数定义:operator 和 config_path 是位置参数 + # 由于脚本需要访问系统级目录,使用 sudo 权限执行 cmd = [ + "sudo", "python3", str(agent_script_path), "comb", -- Gitee From 23dfd5a58fddf13038dedcc8a41331cc8484a312 Mon Sep 17 00:00:00 2001 From: Hongyu Shi Date: Mon, 18 Aug 2025 19:16:17 +0800 Subject: [PATCH 11/12] =?UTF-8?q?refactor(deploy):=20=E6=8F=90=E9=AB=98?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=8F=AF=E8=AF=BB=E6=80=A7=20&=20=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=85=B3=E9=97=AD=20SSL=20=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hongyu Shi --- .../deploy/4-other-script/agent_manager.py | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/scripts/deploy/4-other-script/agent_manager.py b/scripts/deploy/4-other-script/agent_manager.py index fc20690..15d001f 100644 --- a/scripts/deploy/4-other-script/agent_manager.py +++ b/scripts/deploy/4-other-script/agent_manager.py @@ -6,7 +6,7 @@ import os import shutil import sys from enum import Enum -from typing import Any, Dict, List, Optional +from typing import Any, Optional import aiohttp from aiohttp import ClientError, ClientResponseError @@ -59,7 +59,7 @@ class AppPermissionData(BaseModel): alias="visibility", description="可见性(public/private/protected)", ) - users: Optional[List[str]] = Field( + users: list[str] | None = Field( None, alias="authorizedUsers", description="附加人员名单(如果可见性为部分人可见)", @@ -82,28 +82,30 @@ class AppData(BaseModel): icon: str = Field(default="", description="图标") name: str = Field(..., max_length=20, description="应用名称") description: str = Field(..., max_length=150, description="应用简介") - links: List[AppLink] = Field(default=[], description="相关链接", max_length=5) - first_questions: List[str] = Field(default=[], alias="recommendedQuestions", description="推荐问题", max_length=3) + links: list[AppLink] = Field(default=[], description="相关链接", max_length=5) + first_questions: list[str] = Field(default=[], alias="recommendedQuestions", description="推荐问题", max_length=3) history_len: int = Field(3, alias="dialogRounds", ge=1, le=10, description="对话轮次(1~10)") permission: AppPermissionData = Field( - default_factory=lambda: AppPermissionData(authorizedUsers=None), description="权限配置" + default_factory=lambda: AppPermissionData(authorizedUsers=None), + description="权限配置", ) - workflows: List[AppFlowInfo] = Field(default=[], description="工作流信息列表") - mcp_service: List[str] = Field(default=[], alias="mcpService", description="MCP服务id列表") + workflows: list[AppFlowInfo] = Field(default=[], description="工作流信息列表") + mcp_service: list[str] = Field(default=[], alias="mcpService", description="MCP服务id列表") class ApiClient: """API请求客户端封装""" - def __init__(self, base_url: str): + def __init__(self, base_url: str, *, verify_ssl: bool = False) -> None: self.base_url = base_url - self.session = aiohttp.ClientSession() + connector = aiohttp.TCPConnector(ssl=verify_ssl) + self.session = aiohttp.ClientSession(connector=connector) - async def close(self): + async def close(self) -> None: """关闭客户端会话""" await self.session.close() - async def request(self, method: str, path: str, **kwargs) -> Dict[str, Any]: + async def request(self, method: str, path: str, **kwargs) -> dict[str, Any]: """ 带重试机制的异步API请求 @@ -117,6 +119,7 @@ class ApiClient: Raises: ClientError: 当请求失败时 + """ url = f"{self.base_url}{path}" for retry in range(MAX_RETRY_COUNT): @@ -144,6 +147,7 @@ def copy_folder(src_dir: str, dest_dir: str) -> None: Raises: NotADirectoryError: 源路径不是有效的文件夹 RuntimeError: 复制过程中发生错误 + """ if not os.path.isdir(src_dir): raise NotADirectoryError(f"源路径 {src_dir} 不是一个有效的文件夹") @@ -183,6 +187,7 @@ def get_config(config_path: str) -> dict: FileNotFoundError: 配置文件不存在 ValueError: 配置文件格式错误 RuntimeError: 读取文件失败 + """ if not os.path.exists(config_path): raise FileNotFoundError(f"配置文件不存在: {config_path}") @@ -203,7 +208,7 @@ def get_config(config_path: str) -> dict: raise RuntimeError(f"读取配置文件失败: {str(e)}") -async def wait_for_mcp_service(api_client: ApiClient, service_id: str) -> Dict[str, Any]: +async def wait_for_mcp_service(api_client: ApiClient, service_id: str) -> dict[str, Any]: """ 等待MCP服务就绪 @@ -216,6 +221,7 @@ async def wait_for_mcp_service(api_client: ApiClient, service_id: str) -> Dict[s Raises: RuntimeError: 服务超时未就绪 + """ logger.info(f"等待MCP服务就绪: {service_id}") for elapsed in range(SERVICE_WAIT_TIMEOUT): @@ -233,7 +239,7 @@ async def wait_for_mcp_service(api_client: ApiClient, service_id: str) -> Dict[s raise RuntimeError(f"MCP服务 {service_id} 等待超时 ({SERVICE_WAIT_TIMEOUT}秒) 未就绪") -async def delete_mcp_server(api_client: ApiClient, server_id: str) -> Dict[str, Any]: +async def delete_mcp_server(api_client: ApiClient, server_id: str) -> dict[str, Any]: """删除mcp服务""" logger.info(f"删除MCP服务: {server_id}") return await api_client.request("DELETE", f"/api/mcp/{server_id}") @@ -262,6 +268,7 @@ async def process_mcp_config(api_client: ApiClient, config_path: str) -> str: Returns: 生成的server_id + """ config = get_config(config_path) @@ -271,8 +278,8 @@ async def process_mcp_config(api_client: ApiClient, config_path: str) -> str: await delete_mcp_server(api_client, config["serviceId"]) del config["serviceId"] logger.info("已删除旧的MCP服务ID") - except Exception as e: - logger.warning(f"删除旧MCP服务失败(可能不存在),继续创建新服务: {str(e)}") + except Exception: + logger.exception("删除旧MCP服务失败(可能不存在),继续创建新服务") # 创建新服务 server_id = await create_mcp_server(api_client, config) @@ -290,7 +297,7 @@ async def process_mcp_config(api_client: ApiClient, config_path: str) -> str: raise RuntimeError(f"保存配置文件失败: {str(e)}") -async def query_mcp_server(api_client: ApiClient, mcp_id: str) -> Optional[Dict[str, Any]]: +async def query_mcp_server(api_client: ApiClient, mcp_id: str) -> Optional[dict[str, Any]]: """查询MCP服务状态""" logger.debug(f"查询MCP服务状态: {mcp_id}") response = await api_client.request("GET", "/api/mcp") @@ -307,7 +314,7 @@ async def query_mcp_server(api_client: ApiClient, mcp_id: str) -> Optional[Dict[ return None -async def install_mcp_server(api_client: ApiClient, mcp_id: str) -> Dict[str, Any]: +async def install_mcp_server(api_client: ApiClient, mcp_id: str) -> dict[str, Any] | None: """安装mcp服务""" logger.info(f"安装MCP服务: {mcp_id}") response = await api_client.request("GET", "/api/mcp") @@ -323,15 +330,16 @@ async def install_mcp_server(api_client: ApiClient, mcp_id: str) -> Dict[str, An logger.debug(f"开始安装MCP服务{mcp_id}") return await api_client.request("POST", f"/api/mcp/{mcp_id}/install") break + return None -async def activate_mcp_server(api_client: ApiClient, mcp_id: str) -> Dict[str, Any]: +async def activate_mcp_server(api_client: ApiClient, mcp_id: str) -> dict[str, Any]: """激活mcp服务""" logger.info(f"激活MCP服务: {mcp_id}") return await api_client.request("POST", f"/api/mcp/{mcp_id}", json={"active": "true"}) -async def deploy_app(api_client: ApiClient, app_id: str) -> Dict[str, Any]: +async def deploy_app(api_client: ApiClient, app_id: str) -> dict[str, Any]: """发布应用""" logger.info(f"发布应用: {app_id}") return await api_client.request("POST", f"/api/app/{app_id}", json={}) @@ -430,7 +438,7 @@ async def create_agent(api_client: ApiClient, config_path: str) -> None: logger.info("Agent创建流程完成") -async def main_async(): +async def main_async() -> None: parser = argparse.ArgumentParser(description="MCP服务器管理工具") parser.add_argument( "operator", @@ -471,7 +479,7 @@ async def main_async(): sys.exit(0) -def main(): +def main() -> None: try: asyncio.run(main_async()) except KeyboardInterrupt: -- Gitee From f41d7a9b74f3fde7f9643a34d47feb1c67db719a Mon Sep 17 00:00:00 2001 From: Hongyu Shi Date: Tue, 19 Aug 2025 09:37:02 +0800 Subject: [PATCH 12/12] =?UTF-8?q?fix(deploy-script):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E8=AF=AD=E6=B3=95=E9=94=99=E8=AF=AF;=20=E8=B0=83=E6=95=B4=20Ag?= =?UTF-8?q?ent=20=E5=88=9D=E5=A7=8B=E5=8C=96=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hongyu Shi --- scripts/deploy/1-check-env/check_env.sh | 22 +-- .../install_openEulerIntelligence.sh | 16 +-- .../deploy/3-install-server/init_config.sh | 135 +++++++++--------- .../4-other-script/get_install_model.sh | 22 +-- scripts/deploy/deploy.sh | 54 ++++--- 5 files changed, 133 insertions(+), 116 deletions(-) diff --git a/scripts/deploy/1-check-env/check_env.sh b/scripts/deploy/1-check-env/check_env.sh index abd8bbf..17cddb5 100644 --- a/scripts/deploy/1-check-env/check_env.sh +++ b/scripts/deploy/1-check-env/check_env.sh @@ -5,7 +5,7 @@ COLOR_SUCCESS='\033[32m' # 绿色成功 COLOR_ERROR='\033[31m' # 红色错误 COLOR_WARNING='\033[33m' # 黄色警告 COLOR_RESET='\033[0m' # 重置颜色 -INSTALL_MODEL_FILE="/etc/euler_Intelligence_install_model" +INSTALL_MODE_FILE="/etc/euler_Intelligence_install_mode" # 全局模式标记 OFFLINE_MODE=false @@ -79,8 +79,8 @@ check_url_accessibility() { fi fi - # 读取RAG安装状态(依赖之前的read_install_model函数设置RAG_INSTALL变量) - if ! read_install_model; then + # 读取RAG安装状态(依赖之前的read_install_mode函数设置RAG_INSTALL变量) + if ! read_install_mode; then echo -e "${COLOR_WARNING}[WARN] 无法读取安装模式,默认按RAG未启用检测${COLOR_RESET}" local RAG_INSTALL="n" fi @@ -155,7 +155,7 @@ PORTS=(8002) # 读取安装模式并设置端口列表的函数 init_ports_based_on_web() { - if ! read_install_model; then + if ! read_install_mode; then echo -e "${COLOR_WARNING}[Warning] 无法读取安装模式,使用默认端口配置${COLOR_RESET}" return 1 fi @@ -476,16 +476,16 @@ setup_firewall() { return 0 } # 读取安装模式的方法 -read_install_model() { +read_install_mode() { # 检查文件是否存在 - if [ ! -f "$INSTALL_MODEL_FILE" ]; then - echo "web_install=n" >"$INSTALL_MODEL_FILE" - echo "rag_install=n" >>"$INSTALL_MODEL_FILE" + if [ ! -f "$INSTALL_MODE_FILE" ]; then + echo "web_install=n" >"$INSTALL_MODE_FILE" + echo "rag_install=n" >>"$INSTALL_MODE_FILE" fi # 从文件读取配置(格式:key=value) - local web_install=$(grep "web_install=" "$INSTALL_MODEL_FILE" | cut -d'=' -f2) - local rag_install=$(grep "rag_install=" "$INSTALL_MODEL_FILE" | cut -d'=' -f2) + local web_install=$(grep "web_install=" "$INSTALL_MODE_FILE" | cut -d'=' -f2) + local rag_install=$(grep "rag_install=" "$INSTALL_MODE_FILE" | cut -d'=' -f2) # 验证读取结果 if [ -z "$web_install" ] || [ -z "$rag_install" ]; then @@ -500,7 +500,7 @@ read_install_model() { # 示例:根据安装模式执行对应操作(可根据实际需求扩展) install_components() { # 读取安装模式 - read_install_model || return 1 + read_install_mode || return 1 echo -e "${COLOR_INFO}[Info] 检查软件包是否可用${COLOR_RESET}" if [ "$WEB_INSTALL" = "y" ]; then check_web_pkg diff --git a/scripts/deploy/2-install-dependency/install_openEulerIntelligence.sh b/scripts/deploy/2-install-dependency/install_openEulerIntelligence.sh index 3aea318..da65c13 100644 --- a/scripts/deploy/2-install-dependency/install_openEulerIntelligence.sh +++ b/scripts/deploy/2-install-dependency/install_openEulerIntelligence.sh @@ -5,7 +5,7 @@ COLOR_SUCCESS='\033[32m' # 绿色成功 COLOR_ERROR='\033[31m' # 红色错误 COLOR_WARNING='\033[33m' # 黄色警告 COLOR_RESET='\033[0m' # 重置颜色 -INSTALL_MODEL_FILE="/etc/euler_Intelligence_install_model" +INSTALL_MODE_FILE="/etc/euler_Intelligence_install_mode" # 全局变量 declare -a installed_pkgs=() install_success=true @@ -528,15 +528,15 @@ install_web() { fi } # 读取安装模式的方法 -read_install_model() { - if [ ! -f "$INSTALL_MODEL_FILE" ]; then - echo "web_install=n" >"$INSTALL_MODEL_FILE" - echo "rag_install=n" >>"$INSTALL_MODEL_FILE" +read_install_mode() { + if [ ! -f "$INSTALL_MODE_FILE" ]; then + echo "web_install=n" >"$INSTALL_MODE_FILE" + echo "rag_install=n" >>"$INSTALL_MODE_FILE" fi # 从文件读取配置(格式:key=value) - local web_install=$(grep "web_install=" "$INSTALL_MODEL_FILE" | cut -d'=' -f2) - local rag_install=$(grep "rag_install=" "$INSTALL_MODEL_FILE" | cut -d'=' -f2) + local web_install=$(grep "web_install=" "$INSTALL_MODE_FILE" | cut -d'=' -f2) + local rag_install=$(grep "rag_install=" "$INSTALL_MODE_FILE" | cut -d'=' -f2) # 验证读取结果 if [ -z "$web_install" ] || [ -z "$rag_install" ]; then @@ -557,7 +557,7 @@ read_install_model() { # 示例:根据安装模式执行对应操作(可根据实际需求扩展) install_components() { # 读取安装模式 - read_install_model || return 1 + read_install_mode || return 1 # 安装Web界面(如果用户选择) if [ "$WEB_INSTALL" = "y" ]; then diff --git a/scripts/deploy/3-install-server/init_config.sh b/scripts/deploy/3-install-server/init_config.sh index 961a991..9dbd023 100644 --- a/scripts/deploy/3-install-server/init_config.sh +++ b/scripts/deploy/3-install-server/init_config.sh @@ -5,18 +5,15 @@ COLOR_SUCCESS='\033[32m' # 绿色成功 COLOR_ERROR='\033[31m' # 红色错误 COLOR_WARNING='\033[33m' # 黄色警告 COLOR_RESET='\033[0m' # 重置颜色 -INSTALL_MODEL_FILE="/etc/euler_Intelligence_install_model" + +INSTALL_MODE_FILE="/etc/euler_Intelligence_install_mode" + ## 配置参数 -#MYSQL_ROOT_PASSWORD="n6F2tJvvY9Khv16CoybL" # 设置您的MySQL root密码 -#AUTHHUB_USER_PASSWORD="n6F2tJvvY9Khv16CoybL" -#MINIO_ROOT_PASSWORD="ZVzc6xJr3B7HsEUibVBh" -#MONGODB_PASSWORD="YqzzpxJtF5tMAMCrHWw6" -#PGSQL_PASSWORD="6QoJxWoBTL5C6syXhR6k" # 生成随机密码函数 generate_random_password() { # 生成24位随机密码(包含大小写字母、数字和特殊字符) local password=$(tr -dc 'A-Za-z0-9' >$mysql_temp + echo "$AUTHHUB_USER_PASSWORD" >>$mysql_temp return 0 } -# 启用并启动服务(改进版) +# 启用并启动服务 enable_services() { echo -e "${COLOR_INFO}[Info] 启动redis、mysql服务...${COLOR_RESET}" local services=("redis" "mysqld") @@ -83,6 +81,7 @@ enable_services() { fi done } + import_sql_file() { local DB_NAME="oauth2" # 替换为你的数据库名 local DB_USER="root" # 数据库用户名 @@ -129,6 +128,7 @@ import_sql_file() { return 1 fi } + # 配置MySQL configure_mysql() { echo -e "${COLOR_INFO}[Info] 初始化MySQL数据库... ${COLOR_RESET}" @@ -147,10 +147,10 @@ y EOF fi - # 创建authhub用户 - echo -e "${COLOR_INFO}正在创建authhub用户... ${COLOR_RESET}" + # 创建 authhub 用户 + echo -e "${COLOR_INFO}正在创建 authhub 用户... ${COLOR_RESET}" if mysql -u root -e "CREATE USER IF NOT EXISTS 'authhub'@'localhost' IDENTIFIED BY '${AUTHHUB_USER_PASSWORD}'" >/dev/null 2>&1; then - echo -e "${COLOR_SUCCESS} 创建authhub用户成功${COLOR_RESET}" + echo -e "${COLOR_SUCCESS} 创建 authhub 用户成功${COLOR_RESET}" else echo -e "${COLOR_ERROR}[Error] 失败${COLOR_RESET}" echo -e "${COLOR_ERROR}[Error] 错误:无法创建MySQL用户${COLOR_RESET}" @@ -169,10 +169,9 @@ EOF echo -e "${COLOR_ERROR}[Error] 错误:权限设置失败,请检查oauth2数据库是否存在${COLOR_RESET}" return 1 fi - echo -e "${COLOR_SUCCESS}[Success] 初始化MySQL数据库成功 ${COLOR_RESET}" } -#配置nginx +# 配置 nginx configure_nginx() { local nginx_conf="/etc/nginx/conf.d/authhub.nginx.conf" local backup_conf="/etc/nginx/conf.d/authhub.nginx.conf.bak" @@ -227,6 +226,7 @@ configure_nginx() { echo -e "${COLOR_SUCCESS}[Success] Nginx初始化成功!${COLOR_RESET}" return 0 } + # 安装并配置Tika服务 install_tika() { echo -e "${COLOR_INFO}[Info] 开始安装Tika服务...${COLOR_RESET}" @@ -302,50 +302,52 @@ install_tika() { # echo -e "${COLOR_INFO}[Info] 使用命令: systemctl status tika 查看服务状态${COLOR_RESET}" return 0 } -# PostgreSQL配置函数 + +# PostgreSQL 配置函数 configure_postgresql() { - echo -e "${COLOR_INFO}[Info] 开始配置PostgreSQL...${COLOR_RESET}" + echo -e "${COLOR_INFO}[Info] 开始配置 PostgreSQL...${COLOR_RESET}" local pg_service="postgresql" - # 1. 检查并处理PostgreSQL服务状态 - echo -e "${COLOR_INFO}[Info] 检查PostgreSQL服务状态...${COLOR_RESET}" + # 1. 检查并处理 PostgreSQL 服务状态 + echo -e "${COLOR_INFO}[Info] 检查 PostgreSQL 服务状态...${COLOR_RESET}" if systemctl is-active --quiet "$pg_service"; then - echo -e "${COLOR_WARNING}[Warning] PostgreSQL服务正在运行,正在停止服务...${COLOR_RESET}" + echo -e "${COLOR_WARNING}[Warning] PostgreSQL 服务正在运行,正在停止服务...${COLOR_RESET}" systemctl stop "$pg_service" || { - echo -e "${COLOR_ERROR}[Error] 无法停止PostgreSQL服务${COLOR_RESET}" + echo -e "${COLOR_ERROR}[Error] 无法停止 PostgreSQL 服务${COLOR_RESET}" return 1 } fi - # 3. 初始化数据库 - echo -e "${COLOR_INFO}[Info] 初始化PostgreSQL数据库...${COLOR_RESET}" + + # 2. 初始化数据库 + echo -e "${COLOR_INFO}[Info] 初始化 PostgreSQL 数据库...${COLOR_RESET}" /usr/bin/postgresql-setup --initdb || { echo -e "${COLOR_ERROR}[Error] 数据库初始化失败" echo -e "请检查日志文件: /var/lib/pgsql/initdb_postgresql.log${COLOR_RESET}" return 1 } - # 2. 启动服务 - echo -e "${COLOR_INFO}[Info] 启动PostgreSQL服务...${COLOR_RESET}" + # 3. 启动服务 + echo -e "${COLOR_INFO}[Info] 启动 PostgreSQL 服务...${COLOR_RESET}" systemctl enable --now postgresql || { echo -e "${COLOR_ERROR}[Error] 服务启动失败${COLOR_RESET}" return 1 } - # 3. 设置postgres用户密码 - echo -e "${COLOR_INFO}[Info] 设置PostgreSQL密码...${COLOR_RESET}" + # 4. 设置 postgres 用户密码 + echo -e "${COLOR_INFO}[Info] 设置 PostgreSQL 密码...${COLOR_RESET}" sudo -u postgres psql -c "ALTER USER postgres PASSWORD '$PGSQL_PASSWORD';" || { echo -e "${COLOR_ERROR}[Error] 密码设置失败${COLOR_RESET}" return 1 } - # 4. 启用扩展 - echo -e "${COLOR_INFO}[Info] 启用PostgreSQL扩展...${COLOR_RESET}" + # 5. 启用扩展 + echo -e "${COLOR_INFO}[Info] 启用 PostgreSQL 扩展...${COLOR_RESET}" sudo -u postgres psql -c "CREATE EXTENSION zhparser;" || { - echo -e "${COLOR_ERROR}[Error] 无法启用zhparser扩展${COLOR_RESET}" + echo -e "${COLOR_ERROR}[Error] 无法启用 zhparser 扩展${COLOR_RESET}" return 1 } sudo -u postgres psql -c "CREATE EXTENSION vector;" || { - echo -e "${COLOR_ERROR}[Error] 无法启用vector扩展${COLOR_RESET}" + echo -e "${COLOR_ERROR}[Error] 无法启用 vector 扩展${COLOR_RESET}" return 1 } @@ -364,7 +366,7 @@ configure_postgresql() { local pg_hba_conf=$(find / -name pg_hba.conf 2>/dev/null | head -n 1) if [ -z "$pg_hba_conf" ]; then - echo -e "${COLOR_ERROR}[Error] 找不到pg_hba.conf文件${COLOR_RESET}" + echo -e "${COLOR_ERROR}[Error] 找不到 pg_hba.conf 文件${COLOR_RESET}" return 1 fi @@ -375,16 +377,17 @@ configure_postgresql() { sed -i -E 's/(local\s+all\s+all\s+)peer/\1md5/' "$pg_hba_conf" sed -i -E 's/(host\s+all\s+all\s+127\.0\.0\.1\/32\s+)ident/\1md5/' "$pg_hba_conf" sed -i -E 's/(host\s+all\s+all\s+::1\/128\s+)ident/\1md5/' "$pg_hba_conf" - # 2. 启动服务 - echo -e "${COLOR_INFO}[Info] 重启PostgreSQL服务...${COLOR_RESET}" + # 6. 重启服务 + echo -e "${COLOR_INFO}[Info] 重启 PostgreSQL 服务...${COLOR_RESET}" systemctl daemon-reload systemctl restart postgresql || { echo -e "${COLOR_ERROR}[Error] 服务重启失败${COLOR_RESET}" return 1 } - echo -e "${COLOR_SUCCESS}[Success] PostgreSQL配置完成${COLOR_RESET}" + echo -e "${COLOR_SUCCESS}[Success] PostgreSQL 配置完成${COLOR_RESET}" return 0 } + is_x86_architecture() { # 获取系统架构信息(使用 uname -m 或 arch 命令) local arch @@ -401,9 +404,9 @@ is_x86_architecture() { } install_rag() { - echo -e "${COLOR_INFO}[Info] 开始初始化配置 euler-copilot-rag...${COLOR_RESET}" + echo -e "${COLOR_INFO}[Info] 开始初始化配置 euler-copilot-rag...${COLOR_RESET}" - # 2. 配置文件处理 + # 配置文件处理 local env_file="../5-resource/env" local env_target="/etc/euler-copilot-rag/data_chain/env" local service_file="../5-resource/rag.service" @@ -428,27 +431,27 @@ install_rag() { echo -e "${COLOR_WARNING}[Warning] 未找到 service 文件:$service_file${COLOR_RESET}" fi - # 3. 安装图形库依赖(OpenGL) + # 安装图形库依赖(OpenGL) if ! dnf install -y mesa-libGL >/dev/null; then echo -e "${COLOR_WARNING}[Warning] mesa-libGL 安装失败,可能影响图形功能${COLOR_RESET}" fi - # 5. 启动服务 - echo -e "${COLOR_INFO}[Info] 设置并启动rag服务...${COLOR_RESET}" + # 启动服务 + echo -e "${COLOR_INFO}[Info] 设置并启动 rag 服务...${COLOR_RESET}" systemctl daemon-reload systemctl enable --now rag || { - echo -e "${COLOR_ERROR}[Error] rag服务启动失败!${COLOR_RESET}" + echo -e "${COLOR_ERROR}[Error] rag 服务启动失败!${COLOR_RESET}" systemctl status rag --no-pager return 1 } - # 6. 验证服务状态 - echo -e "${COLOR_INFO}[Info] 验证rag服务状态...${COLOR_RESET}" + # 验证服务状态 + echo -e "${COLOR_INFO}[Info] 验证 rag 服务状态...${COLOR_RESET}" if systemctl is-active --quiet rag; then - echo -e "${COLOR_SUCCESS}[Success] rag服务运行正常${COLOR_RESET}" + echo -e "${COLOR_SUCCESS}[Success] rag 服务运行正常${COLOR_RESET}" systemctl status rag --no-pager | grep -E "Active:|Loaded:" else - echo -e "${COLOR_ERROR}[Error] rag服务未运行!${COLOR_RESET}" + echo -e "${COLOR_ERROR}[Error] rag 服务未运行!${COLOR_RESET}" journalctl -u rag --no-pager -n 20 return 1 fi @@ -479,6 +482,7 @@ check_network_reachable() { echo -e "${COLOR_WARNING}[Warning] 网络不可达${COLOR_RESET}" return 1 } + setup_tiktoken_cache() { # 预置的本地资源路径 local local_tiktoken_file="../5-resource/9b5ad71b2ce5302211f9c61530b329a4922fc6a4" @@ -499,7 +503,6 @@ setup_tiktoken_cache() { fi # 3. 复制文件到缓存目录 - # 解压tar文件 dos2unix "$local_tiktoken_file" if ! cp -r "$local_tiktoken_file" "$target_file"; then echo -e "${COLOR_ERROR}[Error] tiktoken.tar 解压失败${COLOR_RESET}" @@ -512,14 +515,15 @@ setup_tiktoken_cache() { } # 6. 设置环境变量(影响当前进程) - #特殊处理改token代码 + # 特殊处理改 token 代码 FILE="/usr/lib/euler-copilot-framework/apps/llm/token.py" token_py_file="../5-resource/token.py" cp $token_py_file $FILE echo -e "${COLOR_SUCCESS}[Success] tiktoken缓存已配置: $target_file${COLOR_RESET}" } + install_framework() { - # 安装前检查 + # 1. 安装前检查 echo -e "${COLOR_INFO}[Info] 开始初始化配置 euler-copilot-framework...${COLOR_RESET}" # 2. 检查并创建必要目录 @@ -537,7 +541,7 @@ install_framework() { echo -e "${COLOR_INFO} [Info] 提取的IP地址: $ip_address" # 4. 获取客户端信息 - #针对代理服务器做特殊处理 + # 针对代理服务器做特殊处理 unset http_proxy https_proxy # 5. 配置文件处理 @@ -562,10 +566,10 @@ install_framework() { } echo -e "${COLOR_INFO}[Info] 更新配置文件参数...${COLOR_RESET}" port=8080 - # 安装Web界面(如果用户选择)配置app_id + # 安装 Web 界面(如果用户选择)配置 app_id if [ "$WEB_INSTALL" = "y" ]; then echo -e "${COLOR_INFO}[Info] 获取客户端凭证...${COLOR_RESET}" - if ! get_client_info_auto $ip_address; then + if ! get_client_info_auto "$ip_address"; then echo -e "${COLOR_ERROR}[Error] 获取客户端凭证失败${COLOR_RESET}" return 1 fi @@ -582,7 +586,7 @@ install_framework() { sed -i "/\[login\.settings\]/,/^\[/ s|host = '.*'|host = 'http://${ip_address}:8000'|" "$framework_file" sed -i "s|login_api = '.*'|login_api = 'http://${ip_address}:8080/api/auth/login'|" $framework_file sed -i "s/domain = '.*'/domain = '$ip_address'/" $framework_file - #添加no_auth参数 + # 添加 no_auth 参数 # 检查文件中是否已存在 [no_auth] 块 if grep -q '^\[no_auth\]$' "$framework_file"; then echo -e "${COLOR_INFO}[Info] 文件中已存在 [no_auth] 配置块,更新内容...${COLOR_RESET}" @@ -600,6 +604,7 @@ install_framework() { # 追加新的配置块到文件末尾 cat <>"$framework_file" + [no_auth] enable = true EOF @@ -628,7 +633,7 @@ EOF echo -e "${COLOR_WARNING}[Warning] 无法设置服务文件权限${COLOR_RESET}" } - #特殊处理,如果 openaipublic.blob.core.windows.net 网络不可达 + # 特殊处理,如果 openaipublic.blob.core.windows.net 网络不可达 # 创建缓存目录(通常是 ~/.cache/tiktoken) check_network_reachable || { setup_tiktoken_cache || echo -e "${COLOR_WARNING}[Warning] 无网络 cl100k_base.tiktoken 文件下载失败,请检查网络${COLOR_RESET}" @@ -667,14 +672,14 @@ uninstall_pkg() { dnf remove -y euler-copilot-rag dnf remove -y euler-copilot-framework } -get_client_info_auto() { +get_client_info_auto() { # 声明全局变量 declare -g client_id="" declare -g client_secret="" # 直接调用Python脚本并传递域名参数 - python3 "../4-other-script/get_client_id_and_secret.py" $1 >client_info.tmp 2>&1 + python3 "../4-other-script/get_client_id_and_secret.py" "$1" >client_info.tmp 2>&1 # 检查Python脚本执行结果 if [ $? -ne 0 ]; then @@ -695,17 +700,18 @@ get_client_info_auto() { return 1 fi } + # 读取安装模式的方法 -read_install_model() { +read_install_mode() { # 检查文件是否存在 - if [ ! -f "$INSTALL_MODEL_FILE" ]; then - echo -e "${COLOR_ERROR}[Error] 安装模式文件不存在: $INSTALL_MODEL_FILE${COLOR_RESET}" + if [ ! -f "$INSTALL_MODE_FILE" ]; then + echo -e "${COLOR_ERROR}[Error] 安装模式文件不存在: $INSTALL_MODE_FILE${COLOR_RESET}" return 1 fi # 从文件读取配置(格式:key=value) - local web_install=$(grep "web_install=" "$INSTALL_MODEL_FILE" | cut -d'=' -f2) - local rag_install=$(grep "rag_install=" "$INSTALL_MODEL_FILE" | cut -d'=' -f2) + local web_install=$(grep "web_install=" "$INSTALL_MODE_FILE" | cut -d'=' -f2) + local rag_install=$(grep "rag_install=" "$INSTALL_MODE_FILE" | cut -d'=' -f2) # 验证读取结果 if [ -z "$web_install" ] || [ -z "$rag_install" ]; then @@ -723,10 +729,11 @@ read_install_model() { RAG_INSTALL=$rag_install return 0 } + # 示例:根据安装模式执行对应操作(可根据实际需求扩展) install_components() { # 读取安装模式 - read_install_model || return 1 + read_install_mode || return 1 # 安装Web界面(如果用户选择) if [ "$WEB_INSTALL" = "y" ]; then @@ -750,6 +757,7 @@ init_rag() { configure_postgresql || exit 1 install_rag || return 1 } + init_web() { cd "$SCRIPT_DIR" || exit 1 enable_services || return 1 @@ -758,7 +766,7 @@ init_web() { cd "$SCRIPT_DIR" || exit 1 ./install_authhub_config.sh || return 1 } -# 主函数 + main() { # 获取脚本所在的绝对路径 SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) @@ -767,11 +775,6 @@ main() { update_password install_components || return 1 install_framework || return 1 - cd "$SCRIPT_DIR" || return 1 - ./install_mcpserver.sh - ./init_mcpserver.sh || { - echo -e "\n${COLOR_WARNING} 初始化agent失败,请检查mcp服务是否可用,使用agent初始化工具创建agent,详见部署文档...${COLOR_RESET}" - } } main "$@" diff --git a/scripts/deploy/4-other-script/get_install_model.sh b/scripts/deploy/4-other-script/get_install_model.sh index 83148c1..47e4129 100644 --- a/scripts/deploy/4-other-script/get_install_model.sh +++ b/scripts/deploy/4-other-script/get_install_model.sh @@ -7,12 +7,12 @@ COLOR_ERROR='\033[31m' # 红色错误 COLOR_RESET='\033[0m' # 重置颜色 # 存储安装模式的文件路径 -INSTALL_MODEL_FILE="/etc/euler_Intelligence_install_model" +INSTALL_MODE_FILE="/etc/euler_Intelligence_install_mode" # 询问用户并保存安装模式 ask_install_options() { # 存储安装模式的文件路径 - INSTALL_MODEL_FILE="/etc/euler_Intelligence_install_model" + INSTALL_MODE_FILE="/etc/euler_Intelligence_install_mode" echo -e "\n${COLOR_INFO}[Info] 请选择附加组件安装选项:${COLOR_RESET}" # 询问是否安装web @@ -42,23 +42,23 @@ ask_install_options() { rag_install=$(echo "$rag_choice" | tr '[:upper:]' '[:lower:]') # 保存到文件(格式:key=value,便于后续读取) - echo "web_install=$web_install" >"$INSTALL_MODEL_FILE" - echo "rag_install=$rag_install" >>"$INSTALL_MODEL_FILE" + echo "web_install=$web_install" >"$INSTALL_MODE_FILE" + echo "rag_install=$rag_install" >>"$INSTALL_MODE_FILE" - echo -e "\n${COLOR_INFO}[Info] 安装模式已保存到: $INSTALL_MODEL_FILE${COLOR_RESET}" + echo -e "\n${COLOR_INFO}[Info] 安装模式已保存到: $INSTALL_MODE_FILE${COLOR_RESET}" return 0 } # 读取安装模式的方法 -read_install_model() { +read_install_mode() { # 检查文件是否存在 - if [ ! -f "$INSTALL_MODEL_FILE" ]; then - echo -e "${COLOR_ERROR}[Error] 安装模式文件不存在: $INSTALL_MODEL_FILE${COLOR_RESET}" + if [ ! -f "$INSTALL_MODE_FILE" ]; then + echo -e "${COLOR_ERROR}[Error] 安装模式文件不存在: $INSTALL_MODE_FILE${COLOR_RESET}" return 1 fi # 从文件读取配置(格式:key=value) - local web_install=$(grep "web_install=" "$INSTALL_MODEL_FILE" | cut -d'=' -f2) - local rag_install=$(grep "rag_install=" "$INSTALL_MODEL_FILE" | cut -d'=' -f2) + local web_install=$(grep "web_install=" "$INSTALL_MODE_FILE" | cut -d'=' -f2) + local rag_install=$(grep "rag_install=" "$INSTALL_MODE_FILE" | cut -d'=' -f2) # 验证读取结果 if [ -z "$web_install" ] || [ -z "$rag_install" ]; then @@ -79,7 +79,7 @@ read_install_model() { # 示例:根据安装模式执行对应操作(可根据实际需求扩展) install_components() { # 读取安装模式 - read_install_model || return 1 + read_install_mode || return 1 # 安装Web界面(如果用户选择) if [ "$WEB_INSTALL" = "y" ]; then diff --git a/scripts/deploy/deploy.sh b/scripts/deploy/deploy.sh index bce7f15..fc93378 100644 --- a/scripts/deploy/deploy.sh +++ b/scripts/deploy/deploy.sh @@ -8,7 +8,9 @@ COLOR_RESET='\033[0m' # 重置颜色 GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' -INSTALL_MODEL_FILE="/etc/euler_Intelligence_install_model" + +INSTALL_MODE_FILE="/etc/euler_Intelligence_install_mode" + # 顶层菜单 show_top_menu() { clear @@ -65,12 +67,13 @@ show_restart_menu() { echo "==============================" echo -n "请输入要重启的服务编号(1-7): " } + # 询问用户并保存安装模式 ask_install_options() { local force_mode="$1" # 接收可选参数(force或空值) - echo $force_mode + echo "$force_mode" # 只有当参数不是force,且存在有效配置时才跳过询问 - if [ "$force_mode" != "force" ] && check_existing_install_model "$INSTALL_MODEL_FILE"; then + if [ "$force_mode" != "force" ] && check_existing_install_mode "$INSTALL_MODE_FILE"; then return 0 # 非强制模式且配置有效,直接返回 fi echo -e "\n${COLOR_INFO}[Info] 请选择附加组件安装选项:${COLOR_RESET}" @@ -102,26 +105,29 @@ ask_install_options() { rag_install=$(echo "$rag_choice" | tr '[:upper:]' '[:lower:]') # 保存到文件(格式:key=value,便于后续读取) - echo "web_install=$web_install" >"$INSTALL_MODEL_FILE" - echo "rag_install=$rag_install" >>"$INSTALL_MODEL_FILE" + echo "web_install=$web_install" >"$INSTALL_MODE_FILE" + echo "rag_install=$rag_install" >>"$INSTALL_MODE_FILE" - echo -e "\n${COLOR_INFO}[Info] 安装模式已保存到: $INSTALL_MODEL_FILE${COLOR_RESET}" + echo -e "\n${COLOR_INFO}[Info] 安装模式已保存到: $INSTALL_MODE_FILE${COLOR_RESET}" return 0 } + # 轻量部署 light_deploy() { # 保存到文件(格式:key=value,便于后续读取) - echo "web_install=n" >"$INSTALL_MODEL_FILE" - echo "rag_install=n" >>"$INSTALL_MODEL_FILE" + echo "web_install=n" >"$INSTALL_MODE_FILE" + echo "rag_install=n" >>"$INSTALL_MODE_FILE" return 0 } + # 全量部署 wight_deploy() { # 保存到文件(格式:key=value,便于后续读取) - echo "web_install=y" >"$INSTALL_MODEL_FILE" - echo "rag_install=y" >>"$INSTALL_MODEL_FILE" + echo "web_install=y" >"$INSTALL_MODE_FILE" + echo "rag_install=y" >>"$INSTALL_MODE_FILE" return 0 } + # 带错误检查的脚本执行函数 run_script_with_check() { local script_path=$1 @@ -162,6 +168,7 @@ run_sub_script() { esac return 0 } + # 执行子菜单选择部署模式对应脚本 run_sub_model_script() { case $1 in @@ -186,8 +193,9 @@ run_sub_model_script() { esac return 0 } + # 检查是否存在有效的安装模式配置文件 -check_existing_install_model() { +check_existing_install_mode() { local target_file="$1" # 检查文件是否存在 @@ -262,6 +270,7 @@ restart_service() { return 3 fi } + # 帮助信息函数 show_help() { echo -e "${GREEN}openEuler Intelligence 一键部署系统使用说明${COLOR_RESET}" @@ -271,10 +280,9 @@ show_help() { echo "" echo -e "${BLUE}选项:${COLOR_RESET}" echo " 无参数 进入交互式菜单" - # echo " --q 切换安装部署模式" echo " --h 显示本帮助信息" echo " --help 同 --h" - echo " --a 进入agent初始化模式,详见部署文档" + echo " --a 进入 Agent 初始化模式,详见部署文档" echo "" echo -e "${BLUE}服务部署手册查看位置:${COLOR_RESET}" echo " 1. 在线文档: https://gitee.com/openeuler/euler-copilot-shell/blob/dev/scripts/deploy/安装部署手册.md" @@ -287,6 +295,7 @@ show_help() { echo "==============================================================================" exit 0 } + agent_manager() { # 获取主脚本绝对路径并切换到所在目录 MAIN_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) @@ -296,7 +305,15 @@ agent_manager() { cd "$MAIN_DIR" || exit 1 fi - # 将所有接收的参数传递给Python脚本 + # 安装 MCP 服务 + 3-install-server/install_mcpserver.sh + # 初始化 MCP 服务 + 3-install-server/init_mcpserver.sh || { + echo -e "\n${COLOR_ERROR} 初始化 Agent 失败,请检查 MCP 服务是否可用,使用 Agent 初始化工具创建 Agent,详见部署文档...${COLOR_RESET}" + exit 1 + } + + # 将所有接收的参数传递给 Python 脚本 python3 4-other-script/agent_manager.py "$@" return 0 } @@ -304,17 +321,14 @@ agent_manager() { # 检查帮助参数 if [[ "$1" == "--h" || "$1" == "--help" ]]; then show_help - return 0 fi -## 检查切换部署方式 -#if [[ "$1" == "--q" ]]; then -# ask_install_options "force" -# return 0 -#fi + +# 检查是否进入 Agent 初始化模式 if [[ "$1" == "--a" ]]; then agent_manager "${@:2}" exit 0 fi + # 获取主脚本绝对路径并切换到所在目录 MAIN_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) if [ "${MAIN_DIR}" = "/usr/bin" ]; then -- Gitee