已合并
support to upload runtime to pypi #103
陈卉创建于 4月20日
support to upload runtime to pypi #103
已合并
共 19 个文件变更+400-338
| @@ -1,41 +1,42 @@ | |||
| 1 | -[build-system] | 1 | +[build-system] |
| 2 | -requires = ["setuptools>=61.0", "wheel"] | 2 | +requires = ["setuptools>=61.0", "wheel"] |
| 3 | -build-backend = "setuptools.build_meta" | 3 | +build-backend = "setuptools.build_meta" |
| 4 | - | 4 | + |
| 5 | -[project] | 5 | +[project] |
| 6 | -name = "lowcode-agent-runner" | 6 | +name = "lowcode-agent-runner" |
| 7 | -version = "0.1.0" | 7 | +version = "0.1.0" |
| 8 | -description = "Lowcode Agent Runner - 从导出的 JSON 配置加载并运行 Agent" | 8 | +description = "Lowcode Agent Runner - 从导出的 JSON 配置加载并运行 Agent" |
| 9 | -requires-python = ">=3.11.4" | 9 | +requires-python = ">=3.11.4" |
| 10 | -dependencies = [ | 10 | +dependencies = [ |
| 11 | - "openjiuwen[chromadb,obs]==0.1.10", | 11 | + "openjiuwen[chromadb,obs]==0.1.10", |
| 12 | - "openjiuwen_studio>=0.1.7", | 12 | + "fastapi==0.115.11", |
| 13 | - "fastapi==0.115.11", | 13 | + "uvicorn[standard]==0.42.0", |
| 14 | - "uvicorn[standard]==0.42.0", | 14 | + "pydantic==2.11.7", |
| 15 | - "pydantic==2.11.7", | 15 | + "pydantic[email]==2.11.7", |
| 16 | - "pydantic[email]==2.11.7", | 16 | + "pydantic-settings==2.5.2", |
| 17 | - "pydantic-settings==2.5.2", | 17 | + "openjiuwen-studio==0.1.7", |
| 18 | -] | 18 | + "openjiuwen-runtime-service==0.1.0" |
| 19 | - | 19 | +] |
| 20 | -[project.scripts] | 20 | + |
| 21 | -lowcode-agent-runner = "openjiuwen_runtime.examples.lowcode_agent.__main__:main" | 21 | +[project.scripts] |
| 22 | - | 22 | +lowcode-agent-runner = "openjiuwen_runtime.examples.lowcode_agent.__main__:main" |
| 23 | -[tool.uv] | 23 | + |
| 24 | -default-groups = ['dev'] | 24 | +[tool.uv] |
| 25 | - | 25 | +default-groups = ['dev'] |
| 26 | -[dependency-groups] | 26 | + |
| 27 | -dev = [ | 27 | +[dependency-groups] |
| 28 | - "coverage==7.8.0", | 28 | +dev = [ |
| 29 | - "pytest==8.3.5", | 29 | + "coverage==7.8.0", |
| 30 | - "pytest-asyncio==1.2.0", | 30 | + "pytest==8.3.5", |
| 31 | - "pytest-mock==3.14.0", | 31 | + "pytest-asyncio==1.2.0", |
| 32 | - "ruff==0.9.10", | 32 | + "pytest-mock==3.14.0", |
| 33 | -] | 33 | + "ruff==0.9.10", |
| 34 | - | 34 | +] |
| 35 | -[[tool.uv.index]] | 35 | + |
| 36 | -url = "https://mirrors.aliyun.com/pypi/simple/" | 36 | +[[tool.uv.index]] |
| 37 | - | 37 | +url = "https://mirrors.aliyun.com/pypi/simple/" |
| 38 | -[tool.setuptools.packages.find] | 38 | + |
| 39 | -where = ["."] | 39 | +[tool.setuptools.packages.find] |
| 40 | -exclude = ["*.egg-info", "tests"] | 40 | +where = ["."] |
| 41 | -include = ["openjiuwen_runtime*"] | 41 | +exclude = ["*.egg-info", "tests"] |
| 42 | +include = ["openjiuwen_runtime*"] | ||
| @@ -12,9 +12,8 @@ dependencies = [ | |||
| 12 | "python-dotenv>=1.0.0", | 12 | "python-dotenv>=1.0.0", |
| 13 | "fastapi>=0.100.0", | 13 | "fastapi>=0.100.0", |
| 14 | "uvicorn>=0.23.0", | 14 | "uvicorn>=0.23.0", |
| 15 | - # local dependencies | 15 | + "openjiuwen-runtime-foundation==0.1.0", |
| 16 | - # "openjiuwen-runtime-management @ file:///${PROJECT_ROOT}/../management", | 16 | + "openjiuwen-runtime-management==0.1.0" |
| 17 | - "openjiuwen-runtime-management==0.0.1", | ||
| 18 | ] | 17 | ] |
| 19 | 18 | ||
| 20 | [tool.uv] | 19 | [tool.uv] |
| @@ -7,45 +7,42 @@ We recommend using Runtime from **source**: clone the repository and use the sta | |||
| 7 | - Python >= 3.11.4 | 7 | - Python >= 3.11.4 |
| 8 | - `uv` >= 0.25.x | 8 | - `uv` >= 0.25.x |
| 9 | - `git` (clone the repository) | 9 | - `git` (clone the repository) |
| 10 | -- `bash` (Linux / macOS) or **PowerShell** (Windows) | 10 | +- `bash` (Linux / macOS) or **Git Bash** (Windows) |
| 11 | 11 | ||
| 12 | ## Start Runtime in one step | 12 | ## Start Runtime in one step |
| 13 | 13 | ||
| 14 | -### 1) Clone the repository | 14 | +### 1) Prepare environment configuration |
| 15 | 15 | ||
| 16 | -```shell | 16 | +Refer to [Configuration](<2. Configuration.md>) to create and edit the `.env` file, and adjust runtime parameters as needed. Key configuration items are as follows: |
| 17 | -git clone https://gitcode.com/openJiuwen/agent-runtime.git | ||
| 18 | -cd agent-runtime | ||
| 19 | -``` | ||
| 20 | - | ||
| 21 | -### 2) Prepare environment configuration | ||
| 22 | - | ||
| 23 | -```shell | ||
| 24 | -cd server | ||
| 25 | -cp .env.example .env | ||
| 26 | -``` | ||
| 27 | - | ||
| 28 | -Edit `server/.env` as needed. Important fields include: | ||
| 29 | 17 | ||
| 30 | - `DB_TYPE`: `mysql` or `sqlite` | 18 | - `DB_TYPE`: `mysql` or `sqlite` |
| 19 | +- Set `MODE` to `product` | ||
| 31 | - Other runtime settings (e.g. IP address, port) | 20 | - Other runtime settings (e.g. IP address, port) |
| 32 | 21 | ||
| 33 | -### 3) Run the startup script | 22 | +Set environment variables: |
| 34 | - | ||
| 35 | -From the **repository root** (`agent-runtime/`): | ||
| 36 | - | ||
| 37 | -**Linux / macOS:** | ||
| 38 | 23 | ||
| 39 | ```shell | 24 | ```shell |
| 40 | -cd .. | 25 | +set -a; source .env; set +a |
| 41 | -bash scripts/run-server.sh | ||
| 42 | ``` | 26 | ``` |
| 43 | 27 | ||
| 44 | -**Windows (PowerShell):** | 28 | +### 2) Install the Runtime package |
| 45 | 29 | ||
| 46 | -```powershell | 30 | +**Linux/MacOS/Windows (Git Bash):** |
| 47 | -cd .. | 31 | + |
| 48 | -.\scripts\run-server.ps1 | 32 | +```shell |
| 33 | +# Create and activate an isolated virtual environment: | ||
| 34 | +uv venv | ||
| 35 | +source .venv/Scripts/activate | ||
| 36 | +# Install the runtime server package: | ||
| 37 | +uv pip install agent-runtime-server | ||
| 38 | +``` | ||
| 39 | + | ||
| 40 | +### 3) Start the Runtime service | ||
| 41 | + | ||
| 42 | +**Linux/MacOS/Windows (Git Bash):** | ||
| 43 | + | ||
| 44 | +```shell | ||
| 45 | +python -m openjiuwen_runtime.server.main | ||
| 49 | ``` | 46 | ``` |
| 50 | 47 | ||
| 51 | By default, Runtime is available on port **8186**. | 48 | By default, Runtime is available on port **8186**. |
| @@ -113,6 +113,21 @@ These control how **runtime-server** starts low-code Agents: **`subprocess`**, * | |||
| 113 | - **`docker`**: use containerized low-code Agents regardless of request details — for containerized deployments; | 113 | - **`docker`**: use containerized low-code Agents regardless of request details — for containerized deployments; |
| 114 | - **`k8s`**: use Kubernetes-orchestrated low-code Agents — for K8s deployments. | 114 | - **`k8s`**: use Kubernetes-orchestrated low-code Agents — for K8s deployments. |
| 115 | 115 | ||
| 116 | +## MODE (Runtime Mode) | ||
| 117 | + | ||
| 118 | +**Purpose:** Only takes effect when DEPLOY_TYPE=subprocess. It determines the loading source of service dependencies. | ||
| 119 | +**Available Values:** dev | product | ||
| 120 | + | ||
| 121 | +- **`dev`**: Applied to local source code deployment scenarios. | ||
| 122 | +- **`product`**: Default value, applied to production environments running after installation from the official PyPI repository. | ||
| 123 | + | ||
| 124 | +**Modification Suggestions**: | ||
| 125 | + | ||
| 126 | +- Use dev for local development and source code debugging. | ||
| 127 | +- Keep the default product in formal production environments. | ||
| 128 | +- This configuration is invalid and will not affect service operation when the deployment type is docker or k8s. | ||
| 129 | +- Restart the runtime-server after modification for changes to take effect. | ||
| 130 | + | ||
| 116 | **Guidance:** Match the value to your actual environment: | 131 | **Guidance:** Match the value to your actual environment: |
| 117 | 132 | ||
| 118 | - Process-based runtime-server → `DEPLOY_TYPE=subprocess` | 133 | - Process-based runtime-server → `DEPLOY_TYPE=subprocess` |
| @@ -3,64 +3,67 @@ Agent Runtime 首版本主要支持低码方式的 Agent 发布:与 openJiuwen | |||
| 3 | 当前版本推荐通过源码方式使用 Runtime,拉取代码后可通过启动脚本一键完成安装与启动。 | 3 | 当前版本推荐通过源码方式使用 Runtime,拉取代码后可通过启动脚本一键完成安装与启动。 |
| 4 | 4 | ||
| 5 | # 环境要求 | 5 | # 环境要求 |
| 6 | -* python>=3.11.4 | ||
| 7 | -* uv>=0.25.x | ||
| 8 | -* git(用于拉取代码) | ||
| 9 | -* bash(Linux/macOS 系统)或 PowerShell(Windows 系统) | ||
| 10 | 6 | ||
| 7 | +- python>=3.11.4 | ||
| 8 | +- uv>=0.25.x | ||
| 9 | +- git(用于拉取代码) | ||
| 10 | +- bash(Linux/macOS 系统)或 Git Bash(Windows 系统) | ||
| 11 | 11 | ||
| 12 | # 一键启动 Runtime 服务 | 12 | # 一键启动 Runtime 服务 |
| 13 | 13 | ||
| 14 | -### 1) 获取代码 | 14 | +### 1) 准备环境配置 |
| 15 | + | ||
| 16 | +请参考 [配置说明](<2. 配置说明.md>) 创建并编辑 `.env` 配置文件,按需调整运行参数,核心配置项如下,重点包括: | ||
| 17 | + | ||
| 18 | +- `DB_TYPE`:支持 `mysql` 和 `sqlite` | ||
| 19 | +- 将`MODE`的值改成`product` | ||
| 20 | +- 其他运行时相关参数(如ip地址、端口号等) | ||
| 21 | + | ||
| 22 | +设置环境变量 | ||
| 15 | 23 | ||
| 16 | ```shell | 24 | ```shell |
| 17 | -git clone https://gitcode.com/openJiuwen/agent-runtime.git | 25 | +set -a; source .env; set +a |
| 18 | -cd agent-runtime | ||
| 19 | ``` | 26 | ``` |
| 20 | 27 | ||
| 21 | -### 2) 准备环境配置 | 28 | +### 2) 安装Runtime包 |
| 29 | + | ||
| 30 | +- Linux/MacOS/Windows(Git Bash): | ||
| 22 | 31 | ||
| 23 | ```shell | 32 | ```shell |
| 24 | -cd agent-runtime/server | 33 | +# 首先初始化并启用独立隔离的虚拟运行环境,实现依赖隔离与版本管控: |
| 25 | -cp .env.example .env | 34 | +uv venv |
| 35 | +source .venv/Scripts/activate | ||
| 36 | +# 安装Runtime包 | ||
| 37 | +uv pip install agent-runtime-server | ||
| 26 | ``` | 38 | ``` |
| 27 | 39 | ||
| 28 | -按需修改 `.env` 中的配置项,重点包括: | 40 | +### 3) 运行Runtime服务: |
| 29 | -* `DB_TYPE`:支持 `mysql` 和 `sqlite` | ||
| 30 | -* 其他运行时相关参数(如ip地址、端口号等) | ||
| 31 | 41 | ||
| 32 | -### 3) 执行启动脚本: | 42 | +- Linux/MacOS/Windows(Git Bash): |
| 33 | 43 | ||
| 34 | -* Linux/MacOS: | 44 | +```shell |
| 35 | - ```shell | 45 | +python -m openjiuwen_runtime.server.main |
| 36 | - bash scripts/run-server.sh | 46 | +``` |
| 37 | - ``` | ||
| 38 | - | ||
| 39 | -* Windows(PowerShell): | ||
| 40 | - ```powershell | ||
| 41 | - .\scripts\run-server.ps1 | ||
| 42 | - ``` | ||
| 43 | 47 | ||
| 44 | 默认启动后可通过 `8186` 端口访问 Runtime 服务。 | 48 | 默认启动后可通过 `8186` 端口访问 Runtime 服务。 |
| 45 | 49 | ||
| 46 | - | ||
| 47 | # 发布智能体 | 50 | # 发布智能体 |
| 48 | -Runtime 支持两种智能体发布方式:低码发布(通过 Studio 一键发布)和高码发布(待开放)。 | ||
| 49 | 51 | ||
| 52 | +Runtime 支持两种智能体发布方式:低码发布(通过 Studio 一键发布)和高码发布(待开放)。 | ||
| 50 | 53 | ||
| 51 | ## 通过studio发布低码智能体 | 54 | ## 通过studio发布低码智能体 |
| 52 | 55 | ||
| 53 | 在 Runtime 服务启动后,需要在 openJiuwen Studio 后端的 `.env` 中配置好 Runtime 的地址和端口,完成对接后即可在 Studio 中一键发布 Agent。 | 56 | 在 Runtime 服务启动后,需要在 openJiuwen Studio 后端的 `.env` 中配置好 Runtime 的地址和端口,完成对接后即可在 Studio 中一键发布 Agent。 |
| 54 | 57 | ||
| 55 | ### 在studio中配置runtime服务连接 | 58 | ### 在studio中配置runtime服务连接 |
| 59 | + | ||
| 56 | 在agent-studio根目录,从`.env.example`复制`.env`文件,打开`.env`文件,修改runtime服务的配置: | 60 | 在agent-studio根目录,从`.env.example`复制`.env`文件,打开`.env`文件,修改runtime服务的配置: |
| 57 | 61 | ||
| 58 | - | 62 | +```shell |
| 59 | - ```shell | 63 | + # Runtime 服务配置(样例) |
| 60 | - # Runtime 服务配置(样例) | 64 | + RUNTIME_HOST=localhost |
| 61 | - RUNTIME_HOST=localhost | 65 | + RUNTIME_PORT=8186 |
| 62 | - RUNTIME_PORT=8186 | 66 | +``` |
| 63 | - ``` | ||
| 64 | 67 | ||
| 65 | ### 发布智能体 | 68 | ### 发布智能体 |
| 66 | 69 | ||
| @@ -85,19 +88,16 @@ Runtime 支持两种智能体发布方式:低码发布(通过 Studio 一键 | |||
| 85 | 88 | ||
| 86 |  | 89 |  |
| 87 | 90 | ||
| 88 | - | ||
| 89 | ### 查看API调用示例 | 91 | ### 查看API调用示例 |
| 90 | 92 | ||
| 91 | api调用页签展示调用运行态智能体的API参数(包括请求方式、url、body、返回结构等)。在请求配置里输入参数,会把你输入的参数自动放入右侧curl/python/javascript示例代码中,便于你直接复制样例代码运行。 | 93 | api调用页签展示调用运行态智能体的API参数(包括请求方式、url、body、返回结构等)。在请求配置里输入参数,会把你输入的参数自动放入右侧curl/python/javascript示例代码中,便于你直接复制样例代码运行。 |
| 92 | 94 | ||
| 93 |  | 95 |  |
| 94 | 96 | ||
| 95 | - | ||
| 96 | ### 下架智能体 | 97 | ### 下架智能体 |
| 97 | 98 | ||
| 98 | 当你不需要该运行态的智能体时,可以在发布管理页标题栏右侧点击 `下架` 按钮, 页面会弹出确认框,点击确认后,系统将移除该智能体的运行态部署。下架成功后,会提示成功并跳转回智能体列表页。 | 99 | 当你不需要该运行态的智能体时,可以在发布管理页标题栏右侧点击 `下架` 按钮, 页面会弹出确认框,点击确认后,系统将移除该智能体的运行态部署。下架成功后,会提示成功并跳转回智能体列表页。 |
| 99 | 100 | ||
| 100 | - | ||
| 101 |  | 101 |  |
| 102 | 102 | ||
| 103 | -## 通过高码发布智能体(待开放) | 103 | +## 通过高码发布智能体(待开放) |
| @@ -126,6 +126,19 @@ | |||
| 126 | 126 | ||
| 127 | - K8s 集群部署的runtime-server: 配置 DEPLOY_TYPE=k8s,同时确保集群环境已配置完成,LOWCODE_IMAGE 配置正确,服务具备 K8s 编排权限。 | 127 | - K8s 集群部署的runtime-server: 配置 DEPLOY_TYPE=k8s,同时确保集群环境已配置完成,LOWCODE_IMAGE 配置正确,服务具备 K8s 编排权限。 |
| 128 | 128 | ||
| 129 | +## MODE(运行模式) | ||
| 130 | + | ||
| 131 | +**作用**:仅在 DEPLOY_TYPE=subprocess 进程部署模式下生效,用于控制低码 Agent 及 Runtime 服务的依赖加载来源,区分本地源码运行与正式包安装运行两种场景。 | ||
| 132 | +可选值:dev、product | ||
| 133 | + | ||
| 134 | +- dev:本地源码部署模式,优先加载项目本地源码与本地依赖,适用于本地开发、调试、二次开发场景; | ||
| 135 | +- product:默认值,生产运行模式,优先从标准依赖环境、PyPI 仓库加载依赖,适用于正式安装部署后的生产环境。 | ||
| 136 | + **修改建议**: | ||
| 137 | + - 本地开发调试、源码直接运行场景,设置 MODE=dev; | ||
| 138 | + - 线上生产环境、通过依赖包安装部署的场景,保持默认 MODE=product; | ||
| 139 | + - 当部署类型为 docker / k8s 时,该配置不生效、无需修改,不影响服务正常运行; | ||
| 140 | + - 修改配置后需重启 runtime-server 服务方可生效。 | ||
| 141 | + | ||
| 129 | # 通用修改注意事项 | 142 | # 通用修改注意事项 |
| 130 | 143 | ||
| 131 | - 所有配置项均为 键=值 格式,等号前后不可有空格,否则会导致参数解析失败; | 144 | - 所有配置项均为 键=值 格式,等号前后不可有空格,否则会导致参数解析失败; |
| @@ -18,7 +18,6 @@ class Settings(BaseSettings): | |||
| 18 | # -------------------------- | 18 | # -------------------------- |
| 19 | DB_TYPE: Literal["mysql", "sqlite"] = Field(default="sqlite", env="DB_TYPE") | 19 | DB_TYPE: Literal["mysql", "sqlite"] = Field(default="sqlite", env="DB_TYPE") |
| 20 | 20 | ||
| 21 | - | ||
| 22 | # -------------------------- | 21 | # -------------------------- |
| 23 | # 【MySQL 配置】(静态可选,DB_TYPE=mysql 时必选) | 22 | # 【MySQL 配置】(静态可选,DB_TYPE=mysql 时必选) |
| 24 | # -------------------------- | 23 | # -------------------------- |
| @@ -42,6 +41,7 @@ class Settings(BaseSettings): | |||
| 42 | PORT: int = Field(default=8186, env="PORT") | 41 | PORT: int = Field(default=8186, env="PORT") |
| 43 | UV_EXTRA_ARGS: str = Field(default="", env="UV_EXTRA_ARGS") | 42 | UV_EXTRA_ARGS: str = Field(default="", env="UV_EXTRA_ARGS") |
| 44 | DEPLOY_TYPE: Literal["subprocess", "docker", "k8s"] = Field(default="subprocess", env="DEPLOY_TYPE") | 43 | DEPLOY_TYPE: Literal["subprocess", "docker", "k8s"] = Field(default="subprocess", env="DEPLOY_TYPE") |
| 44 | + MODE: Literal["dev", "product"] = Field(default="product", env="MODE") | ||
| 45 | 45 | ||
| 46 | # ======================== | 46 | # ======================== |
| 47 | # 内置 对象 | 47 | # 内置 对象 |
| @@ -8,6 +8,7 @@ import subprocess | |||
| 8 | import sys | 8 | import sys |
| 9 | import os | 9 | import os |
| 10 | import shlex | 10 | import shlex |
| 11 | +import time | ||
| 11 | from pathlib import Path | 12 | from pathlib import Path |
| 12 | from typing import Optional | 13 | from typing import Optional |
| 13 | from .log import get_logger | 14 | from .log import get_logger |
| @@ -155,82 +156,100 @@ class VirtualEnvironmentManager: | |||
| 155 | 156 | ||
| 156 | return python_path | 157 | return python_path |
| 157 | 158 | ||
| 158 | - | 159 | + def pip_install(self, deployment_id: str, package: str) -> bool: |
| 159 | - def install_whl(self, deployment_id: str, whl_path: str) -> bool: | ||
| 160 | """ | 160 | """ |
| 161 | - 在虚拟环境中安装WHL包 | 161 | + Install a specified PyPI package in the isolated virtual environment. |
| 162 | + Auto retry on network timeout error, with uv cache resume support. | ||
| 162 | 163 | ||
| 163 | Args: | 164 | Args: |
| 164 | - deployment_id: 部署ID | 165 | + deployment_id: Unique identifier for the deployment |
| 165 | - whl_path: WHL包文件路径 | 166 | + package: Package name with optional version specifier, |
| 167 | + e.g. requests, fastapi==0.115.11, local wheel path | ||
| 166 | 168 | ||
| 167 | Returns: | 169 | Returns: |
| 168 | - 是否安装成功 | 170 | + True if installation succeeded, False otherwise |
| 169 | - | ||
| 170 | - Raises: | ||
| 171 | - RuntimeError: 安装失败 | ||
| 172 | """ | 171 | """ |
| 173 | python_executable = self.get_python_executable(deployment_id) | 172 | python_executable = self.get_python_executable(deployment_id) |
| 174 | - | ||
| 175 | - logger.info("Installing WHL package: %s into %s", whl_path, deployment_id) | ||
| 176 | - | ||
| 177 | uv_extra_args_list = shlex.split(settings.UV_EXTRA_ARGS.strip()) | 173 | uv_extra_args_list = shlex.split(settings.UV_EXTRA_ARGS.strip()) |
| 178 | 174 | ||
| 179 | - # 使用 uv pip 安装包,通过虚拟环境路径指定目标环境 | 175 | + logger.info("Installing package: %s into venv [%s]", package, deployment_id) |
| 176 | + | ||
| 180 | uv_exe = _resolve_uv_executable() | 177 | uv_exe = _resolve_uv_executable() |
| 181 | cmd = [ | 178 | cmd = [ |
| 182 | uv_exe, "pip", "install", | 179 | uv_exe, "pip", "install", |
| 183 | - whl_path, | 180 | + package, |
| 184 | "--python", str(python_executable), | 181 | "--python", str(python_executable), |
| 185 | *uv_extra_args_list | 182 | *uv_extra_args_list |
| 186 | ] | 183 | ] |
| 187 | - logger.debug("Command: %s", " ".join(cmd)) | 184 | + logger.debug("Execute install command: %s", " ".join(cmd)) |
| 188 | 185 | ||
| 189 | - try: | 186 | + max_retry = 3 |
| 190 | - env = os.environ.copy() | 187 | + retry_delay = 5 |
| 191 | - env["VIRTUAL_ENV"] = str(self.get_venv_path(deployment_id)) | 188 | + retry_count = 0 |
| 192 | - result = subprocess.run( | ||
| 193 | - cmd, | ||
| 194 | - env=env, | ||
| 195 | - capture_output=True, | ||
| 196 | - text=True, | ||
| 197 | - encoding='utf-8', | ||
| 198 | - ) | ||
| 199 | 189 | ||
| 200 | - if result.returncode != 0: | 190 | + timeout_keywords = { |
| 201 | - logger.warning( | 191 | + "operation timed out", |
| 202 | - "pip install returned code %s: %s", | 192 | + "network timeout", |
| 203 | - result.returncode, | 193 | + "request or response body error", |
| 204 | - result.stderr, | 194 | + "failed to fetch", |
| 195 | + "timeout" | ||
| 196 | + } | ||
| 197 | + | ||
| 198 | + while retry_count < max_retry: | ||
| 199 | + try: | ||
| 200 | + env = os.environ.copy() | ||
| 201 | + env["VIRTUAL_ENV"] = str(self.get_venv_path(deployment_id)) | ||
| 202 | + # Extended timeout for slow private pypi | ||
| 203 | + env["UV_HTTP_TIMEOUT"] = "600" | ||
| 204 | + env["UV_REQUEST_TIMEOUT"] = "600" | ||
| 205 | + # Disable concurrent download for unstable internal repo | ||
| 206 | + env["UV_CONCURRENT_DOWNLOADS"] = "1" | ||
| 207 | + | ||
| 208 | + result = subprocess.run( | ||
| 209 | + cmd, | ||
| 210 | + env=env, | ||
| 211 | + capture_output=True, | ||
| 212 | + text=True, | ||
| 213 | + encoding="utf-8", | ||
| 205 | ) | 214 | ) |
| 206 | 215 | ||
| 207 | - result = subprocess.run( | 216 | + if result.returncode == 0: |
| 208 | - [str(python_executable), "-m", "pip", "list", "--format=json"], | 217 | + logger.info("Package installed successfully: %s", package) |
| 209 | - capture_output=True, | 218 | + return True |
| 210 | - text=True, | ||
| 211 | - encoding='utf-8', | ||
| 212 | - ) | ||
| 213 | 219 | ||
| 214 | - if result.returncode == 0: | 220 | + # Check whether error is network timeout related |
| 215 | - import json | 221 | + stderr_lower = result.stderr.lower() |
| 216 | - installed_packages = json.loads(result.stdout) | 222 | + is_timeout_error = any(kw in stderr_lower for kw in timeout_keywords) |
| 217 | - whl_name = Path(whl_path).stem.split("-")[0].lower().replace("_", "-") | ||
| 218 | - for pkg in installed_packages: | ||
| 219 | - if pkg["name"].lower().replace("_", "-") == whl_name: | ||
| 220 | - logger.info("WHL package installed successfully: %s", whl_path) | ||
| 221 | - return True | ||
| 222 | 223 | ||
| 223 | - logger.error("WHL package not found in installed list: %s", whl_path) | 224 | + logger.error( |
| 224 | - raise RuntimeError( | 225 | + "Package install failed | exit_code=%s | retry=%s/%s | stderr=%s", |
| 225 | - "Failed to install WHL package: package not in pip list" | 226 | + result.returncode, retry_count + 1, max_retry, result.stderr |
| 226 | - ) from None | 227 | + ) |
| 227 | - else: | 228 | + |
| 228 | - logger.error("Failed to verify installation: %s", result.stderr) | 229 | + if not is_timeout_error: |
| 229 | - raise RuntimeError("Failed to verify WHL installation") from None | 230 | + # Non-timeout error, no retry |
| 231 | + return False | ||
| 232 | + | ||
| 233 | + retry_count += 1 | ||
| 234 | + if retry_count < max_retry: | ||
| 235 | + logger.warning( | ||
| 236 | + "Network timeout detected, retry after %s seconds", | ||
| 237 | + retry_delay | ||
| 238 | + ) | ||
| 239 | + time.sleep(retry_delay) | ||
| 240 | + | ||
| 241 | + except Exception as e: | ||
| 242 | + logger.error( | ||
| 243 | + "Exception occurred while installing package %s: %s", | ||
| 244 | + package, str(e) | ||
| 245 | + ) | ||
| 246 | + retry_count += 1 | ||
| 247 | + if retry_count < max_retry: | ||
| 248 | + time.sleep(retry_delay) | ||
| 249 | + | ||
| 250 | + logger.error("Reached maximum retry limit, install failed: %s", package) | ||
| 251 | + return False | ||
| 230 | 252 | ||
| 231 | - except Exception as e: | ||
| 232 | - logger.error("Failed to install WHL: %s", e) | ||
| 233 | - raise RuntimeError(f"Failed to install WHL package: {e}") from e | ||
| 234 | 253 | ||
| 235 | def delete_venv(self, deployment_id: str) -> bool: | 254 | def delete_venv(self, deployment_id: str) -> bool: |
| 236 | """ | 255 | """ |
| @@ -141,18 +141,22 @@ class LocalSubprocessDeployer(Deployer[SubprocessParams]): | |||
| 141 | venv_path = self.venv_manager.create_venv(deployment_id) | 141 | venv_path = self.venv_manager.create_venv(deployment_id) |
| 142 | logger.info("Virtual environment created: %s", venv_path) | 142 | logger.info("Virtual environment created: %s", venv_path) |
| 143 | 143 | ||
| 144 | - # 获取所有 .whl 文件 | 144 | + if settings.MODE == "dev": |
| 145 | - logger.info("dist_path: %s", settings.dist_path) | 145 | + # 开发模式,本地源码部署: 获取所有 .whl 文件 |
| 146 | - whl_files = list(settings.dist_path.glob("*.whl")) | 146 | + logger.info("dist_path: %s", settings.dist_path) |
| 147 | - if not whl_files: | 147 | + whl_files = list(settings.dist_path.glob("*.whl")) |
| 148 | - raise RuntimeError(f"No .whl files found in dist directory: {settings.dist_path}") | 148 | + if not whl_files: |
| 149 | + raise RuntimeError(f"No .whl files found in dist directory: {settings.dist_path}") | ||
| 149 | 150 | ||
| 150 | - logger.info("whl_files: %s", whl_files) | 151 | + logger.info("whl_files: %s", whl_files) |
| 151 | 152 | ||
| 152 | - # 循环安装所有 whl 包 | 153 | + # 循环安装所有 whl 包 |
| 153 | - for whl_file in whl_files: | 154 | + for whl_file in whl_files: |
| 154 | - self.venv_manager.install_whl(deployment_id, str(whl_file)) | 155 | + self.venv_manager.pip_install(deployment_id, str(whl_file)) |
| 155 | - logger.info("Installed WHL package: %s", whl_file) | 156 | + logger.info("Installed WHL package: %s", whl_file) |
| 157 | + else: | ||
| 158 | + # 从 PyPI仓库安装 lowcode-agent-runner | ||
| 159 | + self.venv_manager.pip_install(deployment_id, "lowcode-agent-runner") | ||
| 156 | 160 | ||
| 157 | # 获取虚拟环境Python解释器 | 161 | # 获取虚拟环境Python解释器 |
| 158 | python_executable = self.venv_manager.get_python_executable(deployment_id) | 162 | python_executable = self.venv_manager.get_python_executable(deployment_id) |
| @@ -1,55 +1,55 @@ | |||
| 1 | -[build-system] | 1 | +[build-system] |
| 2 | -requires = ["setuptools>=61.0", "wheel"] | 2 | +requires = ["setuptools>=61.0", "wheel"] |
| 3 | -build-backend = "setuptools.build_meta" | 3 | +build-backend = "setuptools.build_meta" |
| 4 | - | 4 | + |
| 5 | -[project] | 5 | +[project] |
| 6 | -name = "openjiuwen-runtime-management" | 6 | +name = "openjiuwen-runtime-management" |
| 7 | -version = "0.1.0" | 7 | +version = "0.1.0" |
| 8 | -description = "OpenJiuwen Runtime" | 8 | +description = "OpenJiuwen Runtime" |
| 9 | -requires-python = ">=3.11.4" | 9 | +requires-python = ">=3.11.4" |
| 10 | -dependencies = [ | 10 | +dependencies = [ |
| 11 | - # service | 11 | + # service |
| 12 | - "build>=1.0.0", | 12 | + "build>=1.0.0", |
| 13 | - "aiohttp>=3.9.0", | 13 | + "aiohttp>=3.9.0", |
| 14 | - # config | 14 | + # config |
| 15 | - "pyyaml>=6.0", | 15 | + "pyyaml>=6.0", |
| 16 | - # db | 16 | + # db |
| 17 | - "sqlalchemy==2.0.48", | 17 | + "sqlalchemy==2.0.41", |
| 18 | - "pymysql==1.1.1", | 18 | + "pymysql==1.1.1", |
| 19 | - "cryptography>=42.0.0", | 19 | + "cryptography>=42.0.0", |
| 20 | - "pydantic==2.11.7", | 20 | + "pydantic==2.11.7", |
| 21 | - "pydantic-settings==2.5.2", | 21 | + "pydantic-settings==2.5.2", |
| 22 | - "aiomysql==0.2.0", | 22 | + "aiomysql==0.2.0", |
| 23 | - "aiosqlite==0.21.0", | 23 | + "aiosqlite==0.21.0", |
| 24 | - "redis==7.1.0", | 24 | + "redis==7.1.0", |
| 25 | - # lint | 25 | + # lint |
| 26 | - "ruff==0.9.10", | 26 | + "ruff==0.9.10", |
| 27 | - # local dependency | 27 | + "openjiuwen-runtime-foundation==0.1.0" |
| 28 | -] | 28 | +] |
| 29 | - | 29 | + |
| 30 | -[tool.uv.sources] | 30 | +[tool.uv.sources] |
| 31 | - | 31 | + |
| 32 | -[tool.uv] | 32 | +[tool.uv] |
| 33 | -default-groups = ['dev'] | 33 | +default-groups = ['dev'] |
| 34 | - | 34 | + |
| 35 | -[dependency-groups] | 35 | +[dependency-groups] |
| 36 | -dev = [ | 36 | +dev = [ |
| 37 | - # test | 37 | + # test |
| 38 | - "coverage==7.8.0", | 38 | + "coverage==7.8.0", |
| 39 | - "pytest==8.3.5", | 39 | + "pytest==8.3.5", |
| 40 | - "pytest-asyncio==1.2.0", | 40 | + "pytest-asyncio==1.2.0", |
| 41 | - "pytest-mock==3.14.0", | 41 | + "pytest-mock==3.14.0", |
| 42 | -] | 42 | +] |
| 43 | - | 43 | + |
| 44 | -[[tool.uv.index]] | 44 | +[[tool.uv.index]] |
| 45 | -url = "https://mirrors.aliyun.com/pypi/simple/" | 45 | +url = "https://mirrors.aliyun.com/pypi/simple/" |
| 46 | -# url = "https://cn-north-4_a8fea7d0bff7453bab6394a0842d727c_047e5eaae5e442c4b9e79e3032d2d36e:=j1AK^h4uU@devrepo.devcloud.cn-north-4.huaweicloud.com/artgalaxy/api/pypi/cn-north-4_a8fea7d0bff7453bab6394a0842d727c_pypi_5_2/simple" | 46 | +# url = "https://cn-north-4_a8fea7d0bff7453bab6394a0842d727c_047e5eaae5e442c4b9e79e3032d2d36e:=j1AK^h4uU@devrepo.devcloud.cn-north-4.huaweicloud.com/artgalaxy/api/pypi/cn-north-4_a8fea7d0bff7453bab6394a0842d727c_pypi_5_2/simple" |
| 47 | - | 47 | + |
| 48 | -[tool.setuptools.packages.find] | 48 | +[tool.setuptools.packages.find] |
| 49 | -where = ["."] | 49 | +where = ["."] |
| 50 | -exclude = ["*.egg-info", "tests"] | 50 | +exclude = ["*.egg-info", "tests"] |
| 51 | -include = ["openjiuwen_runtime*"] | 51 | +include = ["openjiuwen_runtime*"] |
| 52 | - | 52 | + |
| 53 | -[tool.pytest.ini_options] | 53 | +[tool.pytest.ini_options] |
| 54 | -testpaths = ["tests"] | 54 | +testpaths = ["tests"] |
| 55 | -pythonpath = ["."] | 55 | +pythonpath = ["."] |
| @@ -43,10 +43,14 @@ else | |||
| 43 | SED_I_FLAG="-i" | 43 | SED_I_FLAG="-i" |
| 44 | fi | 44 | fi |
| 45 | 45 | ||
| 46 | -sed ${SED_I_FLAG} '/openjiuwen-runtime-service/d' ${PROJECT_DIR}/applications/lowcode_agent/pyproject.toml | 46 | +sed ${SED_I_FLAG} '/"openjiuwen-studio==/d' ${PROJECT_DIR}/applications/lowcode_agent/pyproject.toml |
| 47 | -sed ${SED_I_FLAG} '/openjiuwen-runtime-foundation/d' ${PROJECT_DIR}/management/pyproject.toml | 47 | +sed ${SED_I_FLAG} '/"openjiuwen-runtime-service==/d' ${PROJECT_DIR}/applications/lowcode_agent/pyproject.toml |
| 48 | -sed ${SED_I_FLAG} '/openjiuwen-runtime-management/d' ${PROJECT_DIR}/server/pyproject.toml | 48 | +sed ${SED_I_FLAG} '/"openjiuwen-runtime-foundation==/d' ${PROJECT_DIR}/cli/pyproject.toml |
| 49 | - | 49 | +sed ${SED_I_FLAG} '/"openjiuwen-runtime-management==/d' ${PROJECT_DIR}/cli/pyproject.toml |
| 50 | +sed ${SED_I_FLAG} '/"openjiuwen-runtime-foundation==/d' ${PROJECT_DIR}/management/pyproject.toml | ||
| 51 | +sed ${SED_I_FLAG} '/"openjiuwen-runtime-foundation==/d' ${PROJECT_DIR}/server/pyproject.toml | ||
| 52 | +sed ${SED_I_FLAG} '/"openjiuwen-runtime-management==/d' ${PROJECT_DIR}/server/pyproject.toml | ||
| 53 | +sed ${SED_I_FLAG} '/"openjiuwen-runtime-foundation==/d' ${PROJECT_DIR}/service/pyproject.toml | ||
| 50 | 54 | ||
| 51 | # Auto-resolve DIST_DIR: | 55 | # Auto-resolve DIST_DIR: |
| 52 | # Relative path = based on PROJECT_DIR; Absolute path = keep unchanged | 56 | # Relative path = based on PROJECT_DIR; Absolute path = keep unchanged |
| @@ -60,15 +64,11 @@ echo "✅ Final build output directory (absolute path resolved): ${FINAL_DIST_DI | |||
| 60 | rm -rf ${FINAL_DIST_DIR} | 64 | rm -rf ${FINAL_DIST_DIR} |
| 61 | mkdir ${FINAL_DIST_DIR} | 65 | mkdir ${FINAL_DIST_DIR} |
| 62 | 66 | ||
| 63 | -# compile studio package from local workspace when available | 67 | +# complie dist/openjiuwen_studio-0.1.5-py3-none-any.whl |
| 64 | -if [ -d "${PROJECT_DIR}/agent-studio/backend" ]; then | 68 | +cd ${PROJECT_DIR}/agent-studio/backend |
| 65 | - cd ${PROJECT_DIR}/agent-studio/backend | 69 | +uv sync ${UV_EXTRA_ARGS} |
| 66 | - uv sync ${UV_EXTRA_ARGS} | 70 | +rm -rf dist |
| 67 | - rm -rf dist | 71 | +uv build --out-dir ${FINAL_DIST_DIR} ${UV_EXTRA_ARGS} |
| 68 | - uv build --out-dir ${FINAL_DIST_DIR} ${UV_EXTRA_ARGS} | ||
| 69 | -else | ||
| 70 | - echo "⚠️ ${PROJECT_DIR}/agent-studio/backend not found, use openjiuwen_studio from index." | ||
| 71 | -fi | ||
| 72 | 72 | ||
| 73 | # complie dist/openjiuwen-0.1.9-py3-none-any.whl (core library) | 73 | # complie dist/openjiuwen-0.1.9-py3-none-any.whl (core library) |
| 74 | if [ -d "${PROJECT_DIR}/../agent-core" ]; then | 74 | if [ -d "${PROJECT_DIR}/../agent-core" ]; then |
| @@ -0,0 +1,63 @@ | |||
| 1 | +pip config set global.index-url https://repo.huaweicloud.com/repository/pypi/simple | ||
| 2 | +pip config set global.trusted-host repo.huaweicloud.com | ||
| 3 | +pip install uv==0.8.13 -i https://repo.huaweicloud.com/repository/pypi/simple | ||
| 4 | + | ||
| 5 | +# 强制卸载旧版本 | ||
| 6 | +pip uninstall -y setuptools twine -y | ||
| 7 | + | ||
| 8 | +# 安装依赖 | ||
| 9 | +pip install setuptools==82.0.1 wheel==0.46.3 | ||
| 10 | +pip install twine==6.2.0 | ||
| 11 | + | ||
| 12 | +PROJECT_DIR=${PWD} | ||
| 13 | + | ||
| 14 | +# 按依赖顺序构建 | ||
| 15 | +for pkg_dir in foundation management service server applications/lowcode_agent | ||
| 16 | +do | ||
| 17 | + cd "${PROJECT_DIR}/${pkg_dir}" | ||
| 18 | + | ||
| 19 | + # 修改自身版本号 | ||
| 20 | + sed -i 's/^version = .*/version = "'"${VERSION}"'"/' pyproject.toml | ||
| 21 | + | ||
| 22 | + # 修改内部依赖版本号 | ||
| 23 | + case "${pkg_dir}" in | ||
| 24 | + management|service) | ||
| 25 | + sed -i 's/"openjiuwen-runtime-foundation==.*"/"openjiuwen-runtime-foundation=='"${VERSION}"'"/' pyproject.toml | ||
| 26 | + ;; | ||
| 27 | + cli|server) | ||
| 28 | + sed -i 's/"openjiuwen-runtime-foundation==.*"/"openjiuwen-runtime-foundation=='"${VERSION}"'"/' pyproject.toml | ||
| 29 | + sed -i 's/"openjiuwen-runtime-management==.*"/"openjiuwen-runtime-management=='"${VERSION}"'"/' pyproject.toml | ||
| 30 | + ;; | ||
| 31 | + applications/lowcode_agent) | ||
| 32 | + sed -i 's/"openjiuwen-runtime-service==.*"/"openjiuwen-runtime-service=='"${VERSION}"'"/' pyproject.toml | ||
| 33 | + ;; | ||
| 34 | + *) | ||
| 35 | + ;; | ||
| 36 | + esac | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + # 构建(去私有源拉包) | ||
| 40 | + uv sync \ | ||
| 41 | + --trusted-host devrepo.devcloud.cn-north-4.huaweicloud.com \ | ||
| 42 | + -i "https://${USERNAME}:${PASSWORD}@${HUAWEI_PRIVATE_PYPI}/simple/" \ | ||
| 43 | + --index-strategy unsafe-best-match | ||
| 44 | + | ||
| 45 | + uv build --out-dir dist | ||
| 46 | + | ||
| 47 | + # 上传私有仓库 | ||
| 48 | + twine upload \ | ||
| 49 | + --repository-url "https://${HUAWEI_PRIVATE_PYPI}" \ | ||
| 50 | + -u "${USERNAME}" \ | ||
| 51 | + -p "${PASSWORD}" \ | ||
| 52 | + dist/* | ||
| 53 | + | ||
| 54 | + # 官方 PyPI 上传 | ||
| 55 | + echo "UPLOAD_PYPI=${UPLOAD_PYPI}" | ||
| 56 | + if [ "${UPLOAD_PYPI}" == "true" ]; then | ||
| 57 | + twine upload --verbose \ | ||
| 58 | + --repository-url "${PYPI_REPOSITORY}" \ | ||
| 59 | + --username __token__ \ | ||
| 60 | + --password "${PYPI_TOKEN}" \ | ||
| 61 | + dist/* | ||
| 62 | + fi | ||
| 63 | +done | ||
| @@ -50,3 +50,9 @@ UV_EXTRA_ARGS="--native-tls --allow-insecure-host mirrors.aliyun.com -i https:/ | |||
| 50 | # runtime-server 运行在 Kubernetes 集群中,无论部署请求内容如何,仅启动 K8s 编排版 Agent | 50 | # runtime-server 运行在 Kubernetes 集群中,无论部署请求内容如何,仅启动 K8s 编排版 Agent |
| 51 | DEPLOY_TYPE=subprocess | 51 | DEPLOY_TYPE=subprocess |
| 52 | 52 | ||
| 53 | + | ||
| 54 | +# 运行模式(仅在 DEPLOY_TYPE=subprocess 时生效,决定依赖加载来源) | ||
| 55 | +# 可选值:dev | product | ||
| 56 | +# - dev: 适用于本地源码部署 | ||
| 57 | +# - product:默认值,适用于从 PyPI 官方仓库安装后运行的生产环境 | ||
| 58 | +MODE=dev | ||
| @@ -82,14 +82,14 @@ async def health(): | |||
| 82 | 82 | ||
| 83 | 83 | ||
| 84 | def prepare_subprocess_deployment( | 84 | def prepare_subprocess_deployment( |
| 85 | - mode: str, | 85 | + deploy_type: str, |
| 86 | port: int | None | 86 | port: int | None |
| 87 | ) -> tuple[int, Path]: | 87 | ) -> tuple[int, Path]: |
| 88 | """ | 88 | """ |
| 89 | 预处理 subprocess 部署:分配端口、检查端口、获取 WHL 包路径 | 89 | 预处理 subprocess 部署:分配端口、检查端口、获取 WHL 包路径 |
| 90 | 返回:(最终使用的端口, whl文件路径) | 90 | 返回:(最终使用的端口, whl文件路径) |
| 91 | """ | 91 | """ |
| 92 | - if mode != "subprocess": | 92 | + if deploy_type != "subprocess": |
| 93 | return 0, Path("") | 93 | return 0, Path("") |
| 94 | 94 | ||
| 95 | # 端口分配与验证 | 95 | # 端口分配与验证 |
| @@ -105,7 +105,11 @@ def prepare_subprocess_deployment( | |||
| 105 | ) | 105 | ) |
| 106 | logger.info(f"Port: {port}") | 106 | logger.info(f"Port: {port}") |
| 107 | 107 | ||
| 108 | - # 获取 WHL 包路径 | 108 | + # PyPI仓库安装模式 |
| 109 | + if settings.MODE == "product": | ||
| 110 | + return port, Path("") | ||
| 111 | + | ||
| 112 | + # 开发模式,本地源码部署: 获取 WHL 包路径 | ||
| 109 | logger.info(f"dist_path: {settings.dist_path}") | 113 | logger.info(f"dist_path: {settings.dist_path}") |
| 110 | whl_path = settings.dist_path / "lowcode_agent_runner-0.1.0-py3-none-any.whl" | 114 | whl_path = settings.dist_path / "lowcode_agent_runner-0.1.0-py3-none-any.whl" |
| 111 | if not whl_path.exists(): | 115 | if not whl_path.exists(): |
| @@ -1,36 +1,33 @@ | |||
| 1 | -[build-system] | 1 | +[build-system] |
| 2 | -requires = ["setuptools>=61.0", "wheel"] | 2 | +requires = ["setuptools>=61.0", "wheel"] |
| 3 | -build-backend = "setuptools.build_meta" | 3 | +build-backend = "setuptools.build_meta" |
| 4 | - | 4 | + |
| 5 | -[project] | 5 | +[project] |
| 6 | -name = "agent-runtime-server" | 6 | +name = "agent-runtime-server" |
| 7 | -version = "1.0.0" | 7 | +version = "1.0.0" |
| 8 | -description = "Agent Runtime Manager Server" | 8 | +description = "Agent Runtime Manager Server" |
| 9 | -requires-python = ">=3.11.4" | 9 | +requires-python = ">=3.11.4" |
| 10 | -dependencies = [ | 10 | +dependencies = [ |
| 11 | - "openjiuwen-runtime-foundation", | 11 | + "fastapi==0.115.11", |
| 12 | - "fastapi==0.115.11", | 12 | + "uvicorn==0.42.0", |
| 13 | - "uvicorn==0.42.0", | 13 | + "python-dotenv>=1.0.0", |
| 14 | - "python-dotenv>=1.0.0", | 14 | + "python-multipart>=0.0.6", |
| 15 | - "python-multipart>=0.0.6", | 15 | + "openjiuwen-runtime-foundation==0.1.0", |
| 16 | - # local dependencies | 16 | + "openjiuwen-runtime-management==0.1.0" |
| 17 | -] | 17 | +] |
| 18 | - | 18 | + |
| 19 | -[tool.uv.sources] | 19 | +[tool.uv] |
| 20 | -openjiuwen-runtime-foundation = { path = "../foundation", editable = true } | 20 | +default-groups = ['dev'] |
| 21 | - | 21 | + |
| 22 | -[tool.uv] | 22 | +[dependency-groups] |
| 23 | -default-groups = ['dev'] | 23 | +dev = [ |
| 24 | - | 24 | + "ruff==0.9.10", |
| 25 | -[dependency-groups] | 25 | +] |
| 26 | -dev = [ | 26 | + |
| 27 | - "ruff==0.9.10", | 27 | +[[tool.uv.index]] |
| 28 | -] | 28 | +url = "https://mirrors.aliyun.com/pypi/simple/" |
| 29 | - | 29 | + |
| 30 | -[[tool.uv.index]] | 30 | +[tool.setuptools.packages.find] |
| 31 | -url = "https://mirrors.aliyun.com/pypi/simple/" | 31 | +where = ["."] |
| 32 | - | 32 | +exclude = ["*.egg-info", "tests"] |
| 33 | -[tool.setuptools.packages.find] | 33 | +include = ["openjiuwen_runtime*"] |
| 34 | -where = ["."] | ||
| 35 | -exclude = ["*.egg-info", "tests"] | ||
| 36 | -include = ["openjiuwen_runtime*"] | ||
| @@ -1,58 +1,6 @@ | |||
| 1 | # coding: utf-8 | 1 | # coding: utf-8 |
| 2 | # Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved | 2 | # Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved |
| 3 | 3 | ||
| 4 | -""" | ||
| 5 | -openjiuwen-runtime-sdk | ||
| 6 | - | ||
| 7 | -agent-studio 的 Agent 和 Plugin 运行时 SDK。 | ||
| 8 | - | ||
| 9 | -导出: | ||
| 10 | - AgentApp: 用于部署 Agent 的应用类 | ||
| 11 | - PluginApp: 用于部署工具的应用类 | ||
| 12 | - BaseApp: 所有应用的基类 | ||
| 13 | - AppGroup: 多应用容器,共享端口 | ||
| 14 | - QueryRequest: Agent 查询的请求模型 | ||
| 15 | - ResetConversationRequest: 重置对话的请求模型 | ||
| 16 | - | ||
| 17 | -示例 - AgentApp: | ||
| 18 | - from openjiuwen_runtime.sdk import AgentApp | ||
| 19 | - | ||
| 20 | - app = AgentApp("MyAgent") | ||
| 21 | - | ||
| 22 | - | ||
| 23 | - async def init(): | ||
| 24 | - app.agent = MyAgent() | ||
| 25 | - | ||
| 26 | - | ||
| 27 | - async def query(msgs, request): | ||
| 28 | - async for msg, last in app.agent.stream(...): | ||
| 29 | - yield msg, last | ||
| 30 | - | ||
| 31 | - app.run(port=8090) | ||
| 32 | - | ||
| 33 | -示例 - PluginApp: | ||
| 34 | - from openjiuwen_runtime.sdk import PluginApp | ||
| 35 | - | ||
| 36 | - app = PluginApp("CalculatorTools") | ||
| 37 | - | ||
| 38 | - | ||
| 39 | - async def add(a: float, b: float) -> dict: | ||
| 40 | - return {"result": a + b} | ||
| 41 | - | ||
| 42 | - app.run(port=8091) | ||
| 43 | - | ||
| 44 | -示例 - AppGroup: | ||
| 45 | - from openjiuwen_runtime.sdk import AgentApp, AppGroup | ||
| 46 | - | ||
| 47 | - customer_app = AgentApp("CustomerAgent") | ||
| 48 | - assistant_app = AgentApp("AssistantAgent") | ||
| 49 | - | ||
| 50 | - group = AppGroup("MultiAgentGroup") | ||
| 51 | - group.mount("/customer", customer_app) | ||
| 52 | - group.mount("/assistant", assistant_app) | ||
| 53 | - group.run(port=8090) | ||
| 54 | -""" | ||
| 55 | - | ||
| 56 | from .app import BaseApp, AgentApp, PluginApp, AppGroup | 4 | from .app import BaseApp, AgentApp, PluginApp, AppGroup |
| 57 | from .app import Middleware, MiddlewareContext, LoggingMiddleware | 5 | from .app import Middleware, MiddlewareContext, LoggingMiddleware |
| 58 | from .models import QueryRequest, ResetConversationRequest | 6 | from .models import QueryRequest, ResetConversationRequest |
| @@ -8,18 +8,15 @@ version = "0.1.0" | |||
| 8 | description = "OpenJiuwen Runtime" | 8 | description = "OpenJiuwen Runtime" |
| 9 | requires-python = ">=3.11.4" | 9 | requires-python = ">=3.11.4" |
| 10 | dependencies = [ | 10 | dependencies = [ |
| 11 | - "openjiuwen-runtime-foundation", | ||
| 12 | "fastapi==0.115.11", | 11 | "fastapi==0.115.11", |
| 13 | "uvicorn[standard]==0.42.0", | 12 | "uvicorn[standard]==0.42.0", |
| 14 | "pydantic==2.11.7", | 13 | "pydantic==2.11.7", |
| 15 | "pydantic[email]==2.11.7", | 14 | "pydantic[email]==2.11.7", |
| 16 | "pydantic-settings==2.5.2", | 15 | "pydantic-settings==2.5.2", |
| 17 | "greenlet>=3.3.1", | 16 | "greenlet>=3.3.1", |
| 17 | + "openjiuwen-runtime-foundation==0.1.0" | ||
| 18 | ] | 18 | ] |
| 19 | 19 | ||
| 20 | -[tool.uv.sources] | ||
| 21 | -openjiuwen-runtime-foundation = { path = "../foundation", editable = true } | ||
| 22 | - | ||
| 23 | [tool.uv] | 20 | [tool.uv] |
| 24 | default-groups = ['dev'] | 21 | default-groups = ['dev'] |
| 25 | 22 | ||