diff --git "a/docs/development/design/\351\203\250\347\275\262\345\212\251\346\211\213\346\250\241\345\235\227\350\256\276\350\256\241.md" "b/docs/development/design/\351\203\250\347\275\262\345\212\251\346\211\213\346\250\241\345\235\227\350\256\276\350\256\241.md" index d6f8dee94484ab8b5dc9e5d23646b8d362a62f99..3e468375de73b49b00f082c314db13b100a7839c 100644 --- "a/docs/development/design/\351\203\250\347\275\262\345\212\251\346\211\213\346\250\241\345\235\227\350\256\276\350\256\241.md" +++ "b/docs/development/design/\351\203\250\347\275\262\345\212\251\346\211\213\346\250\241\345\235\227\350\256\276\350\256\241.md" @@ -79,7 +79,6 @@ graph TB ```mermaid classDiagram class DeploymentConfig { - +server_ip: str +deployment_mode: str +llm: LLMConfig +embedding: EmbeddingConfig diff --git "a/docs/development/design/\351\241\271\347\233\256\346\225\264\344\275\223\350\256\276\350\256\241.md" "b/docs/development/design/\351\241\271\347\233\256\346\225\264\344\275\223\350\256\276\350\256\241.md" index b4179d6ff681c57b0c330dcc5689c6738603c2d8..2eefdedad36be3ad18c26cf0a83d2943131fc601 100644 --- "a/docs/development/design/\351\241\271\347\233\256\346\225\264\344\275\223\350\256\276\350\256\241.md" +++ "b/docs/development/design/\351\241\271\347\233\256\346\225\264\344\275\223\350\256\276\350\256\241.md" @@ -403,7 +403,6 @@ classDiagram } class DeploymentConfig { - +server_ip: str +enable_web: bool +enable_rag: bool +llm: LLMConfig diff --git a/src/app/deployment/agent.py b/src/app/deployment/agent.py index 31df8132c1044d3af4dfb65b248556fd53d0f06a..b16fc9b9b7b7ed129bfbf076efcbb5d46ec7dd0a 100644 --- a/src/app/deployment/agent.py +++ b/src/app/deployment/agent.py @@ -12,6 +12,7 @@ Agent 管理模块。 from __future__ import annotations import asyncio +import copy import json import subprocess import tomllib @@ -240,14 +241,24 @@ class AgentManager: ) if default_app_id: - self._report_progress( - state, - _("[bold green]智能体初始化完成! 默认 App ID: {app_id}[/bold green]").format( - app_id=default_app_id, - ), - progress_callback, + configured = self._update_default_app_config(default_app_id) + + message = ( + _( + "[bold green]智能体初始化完成! 默认 App ID: {app_id}[/bold green]", + ).format(app_id=default_app_id) + if configured + else _( + "[bold yellow]智能体初始化完成,但默认 App 未写入配置[/bold yellow]", + ) + ) + + self._report_progress(state, message, progress_callback) + logger.info( + "智能体初始化成功完成,默认 App ID: %s,写入配置: %s", + default_app_id, + configured, ) - logger.info("智能体初始化成功完成,默认 App ID: %s", default_app_id) return AgentInitStatus.SUCCESS # 如果没有创建任何智能体,显示警告并返回成功状态 @@ -351,7 +362,7 @@ class AgentManager: "description": config.description, "type": config.mcp_type, "author": config.author, - "config": config.config, + "config": self._normalize_mcp_config(config.config), } config_file = target_dir / "config.json" @@ -378,6 +389,29 @@ class AgentManager: logger.info("MCP 配置写入成功: %s -> %s", config.name, target_dir) return dir_name + def _normalize_mcp_config(self, raw_config: dict[str, Any]) -> dict[str, Any]: + defaults: dict[str, Any] = { + "autoApprove": [], + "disabled": False, + "auto_install": True, + "timeout": 60, + "description": "", + "headers": {}, + } + + if not raw_config: + return copy.deepcopy(defaults) + + merged = {**defaults, **raw_config} + + if not isinstance(merged.get("autoApprove"), list): + merged["autoApprove"] = [] + + if not isinstance(merged.get("headers"), dict): + merged["headers"] = {} + + return merged + async def _write_app_metadata_to_filesystem( self, mcp_service_mapping: dict[str, str], @@ -555,6 +589,17 @@ class AgentManager: logger.info("智能体元数据创建成功: %s (ID: %s)", app_config.name, app_id) return app_id + def _update_default_app_config(self, default_app_id: str) -> bool: + """将默认智能体 ID 写入配置文件""" + try: + self.config_manager.set_default_app(default_app_id) + except Exception: + logger.exception("更新默认智能体配置失败: %s", default_app_id) + return False + + logger.info("默认智能体配置已更新: %s", default_app_id) + return True + def _resolve_mcp_services( self, mcp_paths: list[str], diff --git a/src/app/deployment/models.py b/src/app/deployment/models.py index ac351de7867ae985873976147f72e3e25d649f1e..b1c83376f47c03c743505ff5dafb1b8d9d6515c0 100644 --- a/src/app/deployment/models.py +++ b/src/app/deployment/models.py @@ -65,7 +65,6 @@ class DeploymentConfig: """ # 基础设置 - server_ip: str = "" deployment_mode: str = "light" # light: 轻量部署, full: 全量部署 # LLM 配置 @@ -91,9 +90,6 @@ class DeploymentConfig: """ errors = [] - # 验证基础字段 - errors.extend(self._validate_basic_fields()) - # 验证 LLM 字段 errors.extend(self._validate_llm_fields()) @@ -165,13 +161,6 @@ class DeploymentConfig: return embed_valid, embed_msg, embed_info - def _validate_basic_fields(self) -> list[str]: - """验证基础字段""" - errors = [] - if not self.server_ip.strip(): - errors.append(_("服务器 IP 地址不能为空")) - return errors - def _validate_llm_fields(self) -> list[str]: """验证 LLM 配置字段""" errors = [] diff --git a/src/app/deployment/service.py b/src/app/deployment/service.py index 3986fdf5cd4a4144506beb3f611935ecaf18b8eb..1b59b6fe687860091931840bcd6b3ca19fd00c4c 100644 --- a/src/app/deployment/service.py +++ b/src/app/deployment/service.py @@ -30,6 +30,9 @@ if TYPE_CHECKING: logger = get_logger(__name__) +LOCAL_DEPLOYMENT_HOST = "127.0.0.1" + + class DeploymentResourceManager: """部署资源管理器,管理 RPM 包安装的资源文件""" @@ -97,14 +100,14 @@ class DeploymentResourceManager: toml_data = toml.loads(content) # 更新服务器 IP - server_ip = str(config.server_ip) + server_host = LOCAL_DEPLOYMENT_HOST 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" + toml_data["login"]["settings"]["host"] = f"http://{server_host}:8000" + toml_data["login"]["settings"]["login_api"] = f"http://{server_host}:8080/api/auth/login" # 更新 fastapi 域名 if "fastapi" in toml_data: - toml_data["fastapi"]["domain"] = server_ip + toml_data["fastapi"]["domain"] = server_host # 更新 LLM 配置 if "llm" in toml_data: @@ -422,7 +425,6 @@ class DeploymentService: if not await self._check_and_stop_old_service(progress_callback): return False - # 定义基础部署步骤 steps = [ self._setup_deploy_mode, self._check_environment, @@ -430,23 +432,13 @@ class DeploymentService: self._run_install_dependency_script, self._generate_config_files, self._run_init_config_script, + self._run_agent_init, ] - # 轻量化部署模式下才自动执行 Agent 初始化 - if config.deployment_mode == "light": - steps.append(self._run_agent_init) - - # 依次执行每个步骤 for step in steps: if not await step(config, progress_callback): return False - # 如果是全量部署模式,提示用户到网页端完成 Agent 配置 - if config.deployment_mode == "full": - self.state.add_log(_("✓ 基础服务部署完成")) - self.state.add_log(_("请访问网页管理界面完成 Agent 服务配置")) - self.state.add_log(_("管理界面地址: http://{ip}:8080").format(ip=config.server_ip)) - return True async def _execute_install_command( @@ -842,7 +834,7 @@ class DeploymentService: async def _check_framework_service_health( self, - server_ip: str, + server_host: str, server_port: int, progress_callback: Callable[[DeploymentState], None] | None, ) -> bool: @@ -852,7 +844,7 @@ class DeploymentService: return False # 2. 检查 HTTP API 接口连通性 - return await self._check_framework_api_health(server_ip, server_port, progress_callback) + return await self._check_framework_api_health(server_host, server_port, progress_callback) async def _check_systemctl_service_status( self, @@ -863,10 +855,12 @@ class DeploymentService: check_interval = 2.0 # 2秒 for attempt in range(1, max_attempts + 1): - self.state.add_log(_("检查 oi-runtime 服务状态 ({current}/{total})...").format( - current=attempt, - total=max_attempts, - )) + self.state.add_log( + _("检查 oi-runtime 服务状态 ({current}/{total})...").format( + current=attempt, + total=max_attempts, + ), + ) if progress_callback: progress_callback(self.state) @@ -904,14 +898,14 @@ class DeploymentService: async def _check_framework_api_health( self, - server_ip: str, + server_host: str, server_port: int, progress_callback: Callable[[DeploymentState], None] | None, ) -> bool: """检查 oi-runtime API 健康状态,每10秒检查一次,5分钟后超时""" max_attempts = 30 check_interval = 10.0 # 10秒 - api_url = f"http://{server_ip}:{server_port}/api/user" + api_url = f"http://{server_host}:{server_port}/api/user" http_ok = 200 # HTTP OK 状态码 self.state.add_log(_("等待 openEuler Intelligence 服务就绪")) @@ -955,12 +949,12 @@ class DeploymentService: if progress_callback: progress_callback(self.state) - # 使用配置中的服务器 IP 和默认端口 - server_ip = config.server_ip or "127.0.0.1" + # 使用固定的本地服务地址和默认端口 + server_host = LOCAL_DEPLOYMENT_HOST server_port = 8002 # 检查 openEuler Intelligence 后端服务状态 - if not await self._check_framework_service_health(server_ip, server_port, progress_callback): + if not await self._check_framework_service_health(server_host, server_port, progress_callback): self.state.add_log(_("✗ openEuler Intelligence 服务检查失败")) return False @@ -970,7 +964,7 @@ class DeploymentService: progress_callback(self.state) # 初始化 Agent 和 MCP 服务 - agent_manager = AgentManager(server_ip=server_ip, server_port=server_port) + agent_manager = AgentManager() init_status = await agent_manager.initialize_agents(self.state, progress_callback) if init_status == AgentInitStatus.SUCCESS: @@ -1031,8 +1025,8 @@ class DeploymentService: """ 更新当前用户的配置 - 在部署开始时根据用户填写的服务器IP和部署模式 - 更新 openEuler Intelligence 后端 URL + 在部署开始时根据部署模式 + 更新 openEuler Intelligence 后端的 URL 配置 Args: config: 部署配置 @@ -1042,13 +1036,13 @@ class DeploymentService: config_manager = ConfigManager() # 根据部署配置更新 openEuler Intelligence 后端 URL - server_ip = config.server_ip or "127.0.0.1" + server_host = LOCAL_DEPLOYMENT_HOST if config.deployment_mode == "full": # 全量部署模式:有 nginx,端口是 8080 - eulerintelli_url = f"http://{server_ip}:8080" + eulerintelli_url = f"http://{server_host}:8080" else: # 轻量部署模式:无 nginx,端口是 8002 - eulerintelli_url = f"http://{server_ip}:8002" + eulerintelli_url = f"http://{server_host}:8002" config_manager.set_eulerintelli_url(eulerintelli_url) logger.info("已更新当前用户 openEuler Intelligence 后端 URL: %s", eulerintelli_url) diff --git a/src/app/deployment/ui.py b/src/app/deployment/ui.py index 79a4f750c5182ec2d1b72609fa8e371fac7e342f..3ad6d948500bae0dd0872a7002eac93d3eec9331 100644 --- a/src/app/deployment/ui.py +++ b/src/app/deployment/ui.py @@ -29,7 +29,7 @@ from app.tui_header import OIHeader from i18n.manager import _ from .models import DeploymentConfig, DeploymentState, EmbeddingConfig, LLMConfig -from .service import DeploymentService +from .service import LOCAL_DEPLOYMENT_HOST, DeploymentService if TYPE_CHECKING: from textual.app import ComposeResult @@ -200,12 +200,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", - ) + yield Static(LOCAL_DEPLOYMENT_HOST, classes="form-input") with Horizontal(classes="form-row"): yield Label(_("部署模式:"), classes="form-label") @@ -596,9 +591,6 @@ class DeploymentConfigScreen(ModalScreen[bool]): def _collect_config(self) -> bool: """收集用户配置""" try: - # 基础配置 - self.config.server_ip = self.query_one("#server_ip", Input).value.strip() - # LLM 配置 self.config.llm = LLMConfig( endpoint=self.query_one("#llm_endpoint", Input).value.strip(), diff --git a/tests/app/deployment/test_validate_llm_config.py b/tests/app/deployment/test_validate_llm_config.py index 2ee022895623e467b9fde16af5af9b8e867f0c31..9cda74899f9bae540ff93bbc9a4df6464f7fc45f 100644 --- a/tests/app/deployment/test_validate_llm_config.py +++ b/tests/app/deployment/test_validate_llm_config.py @@ -51,7 +51,6 @@ async def main() -> None: _output("=" * 40) config = DeploymentConfig( - server_ip="127.0.0.1", deployment_mode="light", llm=LLMConfig( endpoint="http://127.0.0.1:1234/v1",