已合并
initial commit #1
王明琦创建于 3月16日
initial commit #1
已合并
共 61 个文件变更+8727-0
| @@ -0,0 +1,116 @@ | |||
| 1 | +# Dependencies | ||
| 2 | +node_modules | ||
| 3 | +npm-debug.log* | ||
| 4 | +yarn-debug.log* | ||
| 5 | +yarn-error.log* | ||
| 6 | +pnpm-debug.log* | ||
| 7 | + | ||
| 8 | +# Build outputs | ||
| 9 | +dist/ | ||
| 10 | +build/ | ||
| 11 | +.next/ | ||
| 12 | +out/ | ||
| 13 | + | ||
| 14 | +# Environment variables | ||
| 15 | +.env | ||
| 16 | +.env.local | ||
| 17 | +.env.development.local | ||
| 18 | +.env.test.local | ||
| 19 | +.env.production.local | ||
| 20 | + | ||
| 21 | +# IDE and editor files | ||
| 22 | +.vscode/ | ||
| 23 | +.idea/ | ||
| 24 | +*.swp | ||
| 25 | +*.swo | ||
| 26 | +*~ | ||
| 27 | + | ||
| 28 | +# OS generated files | ||
| 29 | +.DS_Store | ||
| 30 | +.DS_Store? | ||
| 31 | +._* | ||
| 32 | +.Spotlight-V100 | ||
| 33 | +.Trashes | ||
| 34 | +ehthumbs.db | ||
| 35 | +Thumbs.db | ||
| 36 | + | ||
| 37 | +# Logs | ||
| 38 | +logs | ||
| 39 | +*.log | ||
| 40 | + | ||
| 41 | +# Runtime data | ||
| 42 | +pids | ||
| 43 | +*.pid | ||
| 44 | +*.seed | ||
| 45 | +*.pid.lock | ||
| 46 | + | ||
| 47 | +# Coverage directory used by tools like istanbul | ||
| 48 | +coverage/ | ||
| 49 | +*.lcov | ||
| 50 | + | ||
| 51 | +# nyc test coverage | ||
| 52 | +.nyc_output | ||
| 53 | + | ||
| 54 | +# Dependency directories | ||
| 55 | +jspm_packages/ | ||
| 56 | + | ||
| 57 | +# Optional npm cache directory | ||
| 58 | +.npm | ||
| 59 | + | ||
| 60 | +# Optional eslint cache | ||
| 61 | +.eslintcache | ||
| 62 | + | ||
| 63 | +# Optional REPL history | ||
| 64 | +.node_repl_history | ||
| 65 | + | ||
| 66 | +# Output of 'npm pack' | ||
| 67 | +*.tgz | ||
| 68 | + | ||
| 69 | +# Yarn Integrity file | ||
| 70 | +.yarn-integrity | ||
| 71 | + | ||
| 72 | +# parcel-bundler cache (https://parceljs.org/) | ||
| 73 | +.cache | ||
| 74 | +.parcel-cache | ||
| 75 | + | ||
| 76 | +# Vite cache | ||
| 77 | +.vite | ||
| 78 | + | ||
| 79 | +# TypeScript cache | ||
| 80 | +*.tsbuildinfo | ||
| 81 | + | ||
| 82 | +# uv.lock (keep versioned) | ||
| 83 | +uv.lock | ||
| 84 | + | ||
| 85 | +# Python | ||
| 86 | +*.pyc | ||
| 87 | +*.pyo | ||
| 88 | +*.pyd | ||
| 89 | +__pycache__/ | ||
| 90 | +*.so | ||
| 91 | +.Python | ||
| 92 | +*.db | ||
| 93 | +backend/venv/* | ||
| 94 | +.venv/* | ||
| 95 | +venv/* | ||
| 96 | +ENV/* | ||
| 97 | +env/* | ||
| 98 | +.conda/* | ||
| 99 | +*.egg-info | ||
| 100 | +.eggs/ | ||
| 101 | +dist/ | ||
| 102 | +build/ | ||
| 103 | +*.egg | ||
| 104 | + | ||
| 105 | +# uv temp files | ||
| 106 | +tmpclaude-* | ||
| 107 | + | ||
| 108 | +# pytest | ||
| 109 | +.pytest_cache/ | ||
| 110 | +.coverage | ||
| 111 | +htmlcov/ | ||
| 112 | + | ||
| 113 | +# mypy | ||
| 114 | +.mypy_cache/ | ||
| 115 | +.dmypy.json | ||
| 116 | +dmypy.json | ||
| @@ -0,0 +1,32 @@ | |||
| 1 | +[build-system] | ||
| 2 | +requires = ["setuptools>=61.0", "wheel"] | ||
| 3 | +build-backend = "setuptools.build_meta" | ||
| 4 | + | ||
| 5 | +[project] | ||
| 6 | +name = "openjiuwen-runtime-examples-llm-agent" | ||
| 7 | +version = "0.1.0" | ||
| 8 | +description = "OpenJiuwen Runtime" | ||
| 9 | +requires-python = ">=3.11.4" | ||
| 10 | +dependencies = [ | ||
| 11 | +] | ||
| 12 | + | ||
| 13 | +[tool.uv] | ||
| 14 | +default-groups = ['dev'] | ||
| 15 | + | ||
| 16 | +[dependency-groups] | ||
| 17 | +dev = [ | ||
| 18 | + "coverage==7.8.0", | ||
| 19 | + "pytest==8.3.5", | ||
| 20 | + "pytest-asyncio==1.2.0", | ||
| 21 | + "pytest-mock==3.14.0", | ||
| 22 | + "ruff==0.9.10", | ||
| 23 | +] | ||
| 24 | + | ||
| 25 | +[[tool.uv.index]] | ||
| 26 | +url = "https://mirrors.aliyun.com/pypi/simple/" | ||
| 27 | + | ||
| 28 | +[tool.setuptools] | ||
| 29 | +packages = ["openjiuwen_runtime.examples.llm_agent"] | ||
| 30 | + | ||
| 31 | +[tool.setuptools.package-dir] | ||
| 32 | +"openjiuwen_runtime.examples.llm_agent" = "src" | ||
The file is empty
| @@ -0,0 +1,32 @@ | |||
| 1 | +[build-system] | ||
| 2 | +requires = ["setuptools>=61.0", "wheel"] | ||
| 3 | +build-backend = "setuptools.build_meta" | ||
| 4 | + | ||
| 5 | +[project] | ||
| 6 | +name = "openjiuwen-runtime-examples-workflow-agent" | ||
| 7 | +version = "0.1.0" | ||
| 8 | +description = "OpenJiuwen Runtime" | ||
| 9 | +requires-python = ">=3.11.4" | ||
| 10 | +dependencies = [ | ||
| 11 | +] | ||
| 12 | + | ||
| 13 | +[tool.uv] | ||
| 14 | +default-groups = ['dev'] | ||
| 15 | + | ||
| 16 | +[dependency-groups] | ||
| 17 | +dev = [ | ||
| 18 | + "coverage==7.8.0", | ||
| 19 | + "pytest==8.3.5", | ||
| 20 | + "pytest-asyncio==1.2.0", | ||
| 21 | + "pytest-mock==3.14.0", | ||
| 22 | + "ruff==0.9.10", | ||
| 23 | +] | ||
| 24 | + | ||
| 25 | +[[tool.uv.index]] | ||
| 26 | +url = "https://mirrors.aliyun.com/pypi/simple/" | ||
| 27 | + | ||
| 28 | +[tool.setuptools] | ||
| 29 | +packages = ["openjiuwen_runtime.examples.workflow_agent"] | ||
| 30 | + | ||
| 31 | +[tool.setuptools.package-dir] | ||
| 32 | +"openjiuwen_runtime.examples.workflow_agent" = "src" | ||
The file is empty
| @@ -0,0 +1,59 @@ | |||
| 1 | +# Python | ||
| 2 | +__pycache__/ | ||
| 3 | +*.py[cod] | ||
| 4 | +*$py.class | ||
| 5 | +*.so | ||
| 6 | +.Python | ||
| 7 | +build/ | ||
| 8 | +develop-eggs/ | ||
| 9 | +dist/ | ||
| 10 | +downloads/ | ||
| 11 | +eggs/ | ||
| 12 | +.eggs/ | ||
| 13 | +lib/ | ||
| 14 | +lib64/ | ||
| 15 | +parts/ | ||
| 16 | +sdist/ | ||
| 17 | +var/ | ||
| 18 | +wheels/ | ||
| 19 | +*.egg-info/ | ||
| 20 | +.installed.cfg | ||
| 21 | +*.egg | ||
| 22 | + | ||
| 23 | +# Virtual environments | ||
| 24 | +.venv/ | ||
| 25 | +venv/ | ||
| 26 | +ENV/ | ||
| 27 | +env/ | ||
| 28 | + | ||
| 29 | +# IDE | ||
| 30 | +.vscode/ | ||
| 31 | +.idea/ | ||
| 32 | +*.swp | ||
| 33 | +*.swo | ||
| 34 | +*~ | ||
| 35 | + | ||
| 36 | +# Testing | ||
| 37 | +.pytest_cache/ | ||
| 38 | +.coverage | ||
| 39 | +htmlcov/ | ||
| 40 | +.tox/ | ||
| 41 | + | ||
| 42 | +# Environment | ||
| 43 | +.env | ||
| 44 | +.env.local | ||
| 45 | + | ||
| 46 | +# Database | ||
| 47 | +*.db | ||
| 48 | +*.sqlite | ||
| 49 | +*.sqlite3 | ||
| 50 | + | ||
| 51 | +# Logs | ||
| 52 | +*.log | ||
| 53 | + | ||
| 54 | +# OS | ||
| 55 | +.DS_Store | ||
| 56 | +Thumbs.db | ||
| 57 | + | ||
| 58 | +# Claude | ||
| 59 | +.claude/ | ||
| @@ -0,0 +1,294 @@ | |||
| 1 | +# Agent Runtime Manager | ||
| 2 | + | ||
| 3 | +**openjiuwen-runtime** 的部署管理系统,提供 Agent 和 Plugin 的部署、查询和管理功能。 | ||
| 4 | + | ||
| 5 | +## 核心特性 | ||
| 6 | + | ||
| 7 | +### 虚拟环境隔离 | ||
| 8 | +- 每个部署使用独立的虚拟环境,避免依赖冲突 | ||
| 9 | +- 删除部署时自动清理虚拟环境 | ||
| 10 | +- 支持跨平台(Windows/Linux/Mac) | ||
| 11 | + | ||
| 12 | +### WHL 包部署 | ||
| 13 | +- 标准化的 Python 包分发格式 | ||
| 14 | +- 自动解析包名,无需手动指定 | ||
| 15 | +- 自动安装 WHL 包依赖到独立虚拟环境 | ||
| 16 | + | ||
| 17 | +### 灵活的管理方式 | ||
| 18 | +- **CLI 模式**: 命令行工具,适合本地开发测试 | ||
| 19 | +- **SDK 模式**: Python SDK,适合集成到其他系统 | ||
| 20 | +- **Server 模式**: REST API(预留,供 AgentStudio 调用) | ||
| 21 | + | ||
| 22 | +## 快速开始 | ||
| 23 | + | ||
| 24 | +### 1. 安装 CLI | ||
| 25 | + | ||
| 26 | +```bash | ||
| 27 | +# 进入 CLI 目录 | ||
| 28 | +cd cli | ||
| 29 | + | ||
| 30 | +# 创建虚拟环境并安装依赖 | ||
| 31 | +uv venv | ||
| 32 | +uv sync | ||
| 33 | + | ||
| 34 | +# 安装 CLI(editable 模式) | ||
| 35 | +uv pip install -e ./ | ||
| 36 | +``` | ||
| 37 | + | ||
| 38 | +### 2. 准备 WHL 包 | ||
| 39 | + | ||
| 40 | +```bash | ||
| 41 | +# 构建示例 Agent WHL 包 | ||
| 42 | +cd examples/my_agent | ||
| 43 | +py -m build --outdir ../../dist | ||
| 44 | +``` | ||
| 45 | + | ||
| 46 | +### 3. 部署 Agent | ||
| 47 | + | ||
| 48 | +```bash | ||
| 49 | +# 部署(包名自动从 WHL 解析) | ||
| 50 | +agent-runtime-manager agent deploy "E:/code/agent-runtime/src/manager/examples/dist/my_agent-1.0.0-py3-none-any.whl" | ||
| 51 | + | ||
| 52 | +# 查看部署列表 | ||
| 53 | +agent-runtime-manager agent list | ||
| 54 | + | ||
| 55 | +# 查看部署详情 | ||
| 56 | +agent-runtime-manager agent get <deployment_id> | ||
| 57 | + | ||
| 58 | +# 删除部署 | ||
| 59 | +agent-runtime-manager agent delete <deployment_id> | ||
| 60 | +``` | ||
| 61 | + | ||
| 62 | +## CLI 使用 | ||
| 63 | + | ||
| 64 | +### Agent 命令 | ||
| 65 | + | ||
| 66 | +```bash | ||
| 67 | +# 部署 Agent(自动解析包名) | ||
| 68 | +agent-runtime-manager agent deploy <whl_path> [--port PORT] | ||
| 69 | + | ||
| 70 | +# 查询列表 | ||
| 71 | +agent-runtime-manager agent list [--status STATUS] | ||
| 72 | + | ||
| 73 | +# 获取详情 | ||
| 74 | +agent-runtime-manager agent get <deployment_id> | ||
| 75 | + | ||
| 76 | +# 删除部署 | ||
| 77 | +agent-runtime-manager agent delete <deployment_id> | ||
| 78 | +``` | ||
| 79 | + | ||
| 80 | +### Plugin 命令 | ||
| 81 | + | ||
| 82 | +```bash | ||
| 83 | +# 部署 Plugin | ||
| 84 | +agent-runtime-manager plugin deploy <whl_path> [--port PORT] | ||
| 85 | + | ||
| 86 | +# 查询列表 | ||
| 87 | +agent-runtime-manager plugin list [--status STATUS] | ||
| 88 | + | ||
| 89 | +# 获取详情 | ||
| 90 | +agent-runtime-manager plugin get <deployment_id> | ||
| 91 | + | ||
| 92 | +# 删除部署 | ||
| 93 | +agent-runtime-manager plugin delete <deployment_id> | ||
| 94 | +``` | ||
| 95 | + | ||
| 96 | +## SDK 使用 | ||
| 97 | + | ||
| 98 | +```python | ||
| 99 | +from openjiuwen_runtime.management.sdk import DeploymentManager | ||
| 100 | + | ||
| 101 | +manager = DeploymentManager() | ||
| 102 | + | ||
| 103 | +# 部署 Agent(包名自动解析) | ||
| 104 | +result = await manager.deploy_agent( | ||
| 105 | + whl_path="./my_agent-1.0.0-py3-none-any.whl", | ||
| 106 | + port=8090 | ||
| 107 | +) | ||
| 108 | + | ||
| 109 | +# 查询列表 | ||
| 110 | +deployments = await manager.list_deployments( | ||
| 111 | + deployment_type=DeploymentType.AGENT, | ||
| 112 | + status=DeploymentStatus.RUNNING | ||
| 113 | +) | ||
| 114 | + | ||
| 115 | +# 获取详情 | ||
| 116 | +deployment = await manager.get_deployment(deployment_id) | ||
| 117 | + | ||
| 118 | +# 删除部署 | ||
| 119 | +await manager.delete_deployment(deployment_id) | ||
| 120 | +``` | ||
| 121 | + | ||
| 122 | +## WHL 包规范 | ||
| 123 | + | ||
| 124 | +### 命名规范 | ||
| 125 | + | ||
| 126 | +``` | ||
| 127 | +{package_name}-{version}-{python_tag}-{abi_tag}-{platform_tag}.whl | ||
| 128 | + | ||
| 129 | +示例: | ||
| 130 | +my_agent-1.0.0-py3-none-any.whl | ||
| 131 | +weather_plugin-2.1.0-py3-none-any.whl | ||
| 132 | +``` | ||
| 133 | + | ||
| 134 | +### 目录结构 | ||
| 135 | + | ||
| 136 | +``` | ||
| 137 | +my_agent/ | ||
| 138 | +├── my_agent/ # ✅ 包子目录(必须) | ||
| 139 | +│ ├── __init__.py | ||
| 140 | +│ └── __main__.py # ✅ 主入口文件(必须) | ||
| 141 | +└── setup.py # ✅ 打包配置(必须) | ||
| 142 | +``` | ||
| 143 | + | ||
| 144 | +### __main__.py 要求 | ||
| 145 | + | ||
| 146 | +```python | ||
| 147 | +import argparse | ||
| 148 | + | ||
| 149 | +def main(): | ||
| 150 | + # 1. 必须解析 --host 和 --port 参数 | ||
| 151 | + parser = argparse.ArgumentParser() | ||
| 152 | + parser.add_argument("--host", default="127.0.0.1") | ||
| 153 | + parser.add_argument("--port", type=int, default=8090) | ||
| 154 | + args = parser.parse_args() | ||
| 155 | + | ||
| 156 | + # 2. 必须实现长期运行的服务 | ||
| 157 | + # 例如:FastAPI、Flask 等 HTTP 服务器 | ||
| 158 | + run_server(host=args.host, port=args.port) | ||
| 159 | + | ||
| 160 | +if __name__ == "__main__": | ||
| 161 | + main() | ||
| 162 | +``` | ||
| 163 | + | ||
| 164 | +### setup.py 示例 | ||
| 165 | + | ||
| 166 | +```python | ||
| 167 | +from setuptools import setup, find_packages | ||
| 168 | + | ||
| 169 | +setup( | ||
| 170 | + name="my_agent", | ||
| 171 | + version="1.0.0", | ||
| 172 | + packages=find_packages(), | ||
| 173 | + install_requires=[ | ||
| 174 | + "fastapi>=0.100.0", | ||
| 175 | + "uvicorn>=0.23.0", | ||
| 176 | + ], | ||
| 177 | + python_requires=">=3.8", | ||
| 178 | +) | ||
| 179 | +``` | ||
| 180 | + | ||
| 181 | +## 虚拟环境管理 | ||
| 182 | + | ||
| 183 | +### 目录结构 | ||
| 184 | + | ||
| 185 | +``` | ||
| 186 | +venvs/ | ||
| 187 | +├── agent_20250129_abc123/ # Agent 部署虚拟环境 | ||
| 188 | +│ ├── Scripts/python.exe # Windows | ||
| 189 | +│ ├── Lib/site-packages/ # 已安装的包 | ||
| 190 | +│ └── pyvenv.cfg | ||
| 191 | +└── plugin_20250129_def456/ # Plugin 部署虚拟环境 | ||
| 192 | + ├── bin/python # Linux/Mac | ||
| 193 | + └── ... | ||
| 194 | +``` | ||
| 195 | + | ||
| 196 | +### 自动清理 | ||
| 197 | + | ||
| 198 | +- 部署成功后,虚拟环境会保留直到部署被删除 | ||
| 199 | +- 部署失败时,虚拟环境会自动清理 | ||
| 200 | +- 删除部署时,虚拟环境会自动清理 | ||
| 201 | + | ||
| 202 | +## 配置选项 | ||
| 203 | + | ||
| 204 | +### 环境变量 (.env) | ||
| 205 | + | ||
| 206 | +```bash | ||
| 207 | +# MySQL 数据库 | ||
| 208 | +DB_HOST=124.71.229.79 | ||
| 209 | +DB_PORT=33306 | ||
| 210 | +DB_USER=root | ||
| 211 | +DB_PASSWORD=root | ||
| 212 | +DB_NAME=jiuwen_runtime | ||
| 213 | + | ||
| 214 | +# 虚拟环境 | ||
| 215 | +V_ENVS_ROOT=./venvs # 虚拟环境根目录(默认: ./venvs) | ||
| 216 | +``` | ||
| 217 | + | ||
| 218 | +## 项目架构 | ||
| 219 | + | ||
| 220 | +``` | ||
| 221 | +Agent Runtime Manager | ||
| 222 | +├── sdk/ # Manager SDK(核心共享组件) | ||
| 223 | +│ ├── manager.py # DeploymentManager - 部署管理核心 | ||
| 224 | +│ ├── deployer/ # LocalSubprocessDeployer - WHL 包部署器 | ||
| 225 | +│ ├── database/ # MySQLDatabase - 数据持久化 | ||
| 226 | +│ ├── models/ # Deployment/DeploymentDB - 数据模型 | ||
| 227 | +│ └── utils/ # 工具类 | ||
| 228 | +│ ├── venv_manager.py # 虚拟环境管理 | ||
| 229 | +│ ├── whl_parser.py # WHL 包名解析 | ||
| 230 | +│ ├── port_manager.py # 端口分配 | ||
| 231 | +│ └── id_generator.py # ID 生成 | ||
| 232 | +├── cli/ # CLI 命令行工具 | ||
| 233 | +│ └── src/main.py # Click 命令(agent/plugin deploy/list/get/delete) | ||
| 234 | +└── server/ # REST API Server(预留) | ||
| 235 | +``` | ||
| 236 | + | ||
| 237 | +## 开发指南 | ||
| 238 | + | ||
| 239 | +### 构建示例包 | ||
| 240 | + | ||
| 241 | +```bash | ||
| 242 | +cd examples/my_agent | ||
| 243 | +py -m build --outdir ../../dist | ||
| 244 | +``` | ||
| 245 | + | ||
| 246 | +### 运行测试 | ||
| 247 | + | ||
| 248 | +```bash | ||
| 249 | +cd cli | ||
| 250 | +agent-runtime-manager agent deploy "../examples/dist/my_agent-1.0.0-py3-none-any.whl" | ||
| 251 | +``` | ||
| 252 | + | ||
| 253 | +## 常见问题 | ||
| 254 | + | ||
| 255 | +### 1. Editable 安装问题 | ||
| 256 | + | ||
| 257 | +**症状**: 修改代码后仍然使用旧版本 | ||
| 258 | + | ||
| 259 | +**解决**: | ||
| 260 | +```bash | ||
| 261 | +cd cli | ||
| 262 | +rm -rf .venv/Lib/site-packages/openjiuwen_runtime | ||
| 263 | +uv pip install -e ../sdk | ||
| 264 | +``` | ||
| 265 | + | ||
| 266 | +### 2. WHL 包结构错误 | ||
| 267 | + | ||
| 268 | +**症状**: `No module named xxx` | ||
| 269 | + | ||
| 270 | +**解决**: 确保 WHL 包内有正确的包子目录结构 | ||
| 271 | + | ||
| 272 | +```bash | ||
| 273 | +# 验证 WHL 包内容 | ||
| 274 | +python -m zipfile -l my_agent-1.0.0-py3-none-any.whl | ||
| 275 | +``` | ||
| 276 | + | ||
| 277 | +### 3. 进程立即退出 | ||
| 278 | + | ||
| 279 | +**症状**: 部署后进程很快退出 | ||
| 280 | + | ||
| 281 | +**解决**: `__main__.py` 必须实现长期运行的服务(如 HTTP 服务器) | ||
| 282 | + | ||
| 283 | +## 依赖项 | ||
| 284 | + | ||
| 285 | +### SDK 依赖 | ||
| 286 | +- `sqlalchemy>=2.0.0` - ORM 框架 | ||
| 287 | +- `pymysql>=1.0.0` - MySQL 驱动 | ||
| 288 | +- `pydantic>=2.0.0` - 数据验证 | ||
| 289 | +- `cryptography>=42.0.0` - 加密工具 | ||
| 290 | + | ||
| 291 | +### CLI 依赖 | ||
| 292 | +- `click>=8.0.0` - CLI 框架 | ||
| 293 | +- `python-dotenv>=1.0.0` - 环境变量 | ||
| 294 | +- `fastapi>=0.100.0` - Web 框架(Server 预留) | ||
| @@ -0,0 +1,37 @@ | |||
| 1 | +[build-system] | ||
| 2 | +requires = ["setuptools>=61.0", "wheel"] | ||
| 3 | +build-backend = "setuptools.build_meta" | ||
| 4 | + | ||
| 5 | +[project] | ||
| 6 | +name = "agent-runtime-manager-cli" | ||
| 7 | +version = "0.1.0" | ||
| 8 | +description = "Agent Runtime Manager CLI" | ||
| 9 | +requires-python = ">=3.11.4" | ||
| 10 | +dependencies = [ | ||
| 11 | + "click>=8.0.0", | ||
| 12 | + "python-dotenv>=1.0.0", | ||
| 13 | + "fastapi>=0.100.0", | ||
| 14 | + "uvicorn>=0.23.0", | ||
| 15 | + # SDK dependency (local path) | ||
| 16 | + "openjiuwen-runtime-management-sdk @ file:///${PROJECT_ROOT}/../sdk", | ||
| 17 | +] | ||
| 18 | + | ||
| 19 | +[tool.uv] | ||
| 20 | +default-groups = ['dev'] | ||
| 21 | + | ||
| 22 | +[dependency-groups] | ||
| 23 | +dev = [ | ||
| 24 | + "ruff==0.9.10", | ||
| 25 | +] | ||
| 26 | + | ||
| 27 | +[[tool.uv.index]] | ||
| 28 | +url = "https://mirrors.aliyun.com/pypi/simple/" | ||
| 29 | + | ||
| 30 | +[tool.setuptools] | ||
| 31 | +packages = ["openjiuwen_runtime.cli"] | ||
| 32 | + | ||
| 33 | +[tool.setuptools.package-dir] | ||
| 34 | +"openjiuwen_runtime.cli" = "src" | ||
| 35 | + | ||
| 36 | +[project.scripts] | ||
| 37 | +agent-runtime-manager = "openjiuwen_runtime.cli.main:cli" | ||
| @@ -0,0 +1,100 @@ | |||
| 1 | +@echo off | ||
| 2 | +REM ======================================== | ||
| 3 | +REM Agent Runtime Manager - Rebuild Script | ||
| 4 | +REM ======================================== | ||
| 5 | +REM 彻底清除缓存并重新编译 CLI 和 Server,解决 editable install 缓存问题 | ||
| 6 | + | ||
| 7 | +set SCRIPT_DIR=%~dp0 | ||
| 8 | +set PROJECT_ROOT=%SCRIPT_DIR%.. | ||
| 9 | + | ||
| 10 | +echo ======================================== | ||
| 11 | +echo 开始清理缓存... | ||
| 12 | +echo ======================================== | ||
| 13 | + | ||
| 14 | +REM 1. 清除 CLI venv 中的缓存 | ||
| 15 | +echo [1/7] 清除 CLI venv 缓存... | ||
| 16 | +cd "%PROJECT_ROOT%\cli" | ||
| 17 | +if exist ".venv\Lib\site-packages\openjiuwen_runtime" ( | ||
| 18 | + rd /s /q ".venv\Lib\site-packages\openjiuwen_runtime" | ||
| 19 | + echo - 已删除 CLI venv 中的 openjiuwen_runtime | ||
| 20 | +) | ||
| 21 | +for /d /r .venv %%d in (__pycache__) do @if exist "%%d" ( | ||
| 22 | + rd /s /q "%%d" 2>nul | ||
| 23 | +) | ||
| 24 | +for /r .venv %%f in (*.pyc) do @if exist "%%f" ( | ||
| 25 | + del /f /q "%%f" 2>nul | ||
| 26 | +) | ||
| 27 | + | ||
| 28 | +REM 2. 清除 Server venv 中的缓存 | ||
| 29 | +echo [2/7] 清除 Server venv 缓存... | ||
| 30 | +cd "%PROJECT_ROOT%\server" | ||
| 31 | +if exist ".venv\Lib\site-packages\openjiuwen_runtime" ( | ||
| 32 | + rd /s /q ".venv\Lib\site-packages\openjiuwen_runtime" | ||
| 33 | + echo - 已删除 Server venv 中的 openjiuwen_runtime | ||
| 34 | +) | ||
| 35 | +for /d /r .venv %%d in (__pycache__) do @if exist "%%d" ( | ||
| 36 | + rd /s /q "%%d" 2>nul | ||
| 37 | +) | ||
| 38 | +for /r .venv %%f in (*.pyc) do @if exist "%%f" ( | ||
| 39 | + del /f /q "%%f" 2>nul | ||
| 40 | +) | ||
| 41 | + | ||
| 42 | +REM 3. 清除 SDK 源码中的 pyc 缓存 | ||
| 43 | +echo [3/7] 清除 SDK 源码缓存... | ||
| 44 | +cd "%PROJECT_ROOT%\sdk" | ||
| 45 | +for /d /r %%d in (__pycache__) do @if exist "%%d" ( | ||
| 46 | + rd /s /q "%%d" 2>nul | ||
| 47 | +) | ||
| 48 | +for /r %%f in (*.pyc) do @if exist "%%f" ( | ||
| 49 | + del /f /q "%%f" 2>nul | ||
| 50 | +) | ||
| 51 | + | ||
| 52 | +REM 4. 卸载旧版本 SDK (CLI) | ||
| 53 | +echo [4/7] 卸载 CLI 旧版本包... | ||
| 54 | +cd "%PROJECT_ROOT%\cli" | ||
| 55 | +call uv pip uninstall openjiuwen-runtime-management-sdk 2>nul | ||
| 56 | +call uv pip uninstall agent-runtime-manager-cli 2>nul | ||
| 57 | + | ||
| 58 | +REM 5. 重新安装 SDK (CLI) | ||
| 59 | +echo [5/7] 安装 SDK 到 CLI (editable mode)... | ||
| 60 | +call uv pip install -e "%PROJECT_ROOT%\sdk" | ||
| 61 | + | ||
| 62 | +REM 6. 重新安装 CLI | ||
| 63 | +echo [6/7] 安装 CLI... | ||
| 64 | +call uv pip install -e "./" | ||
| 65 | + | ||
| 66 | +REM 7. 重新安装 SDK (Server) - 使用 --python 指定Server的venv | ||
| 67 | +echo [7/7] 安装 SDK 到 Server (editable mode)... | ||
| 68 | +cd "%PROJECT_ROOT%\server" | ||
| 69 | +if exist ".venv\Scripts\python.exe" ( | ||
| 70 | + call uv pip install --python .venv\Scripts\python.exe -e "%PROJECT_ROOT%\sdk" | ||
| 71 | +) else ( | ||
| 72 | + echo ! Server venv 不存在,跳过 | ||
| 73 | +) | ||
| 74 | + | ||
| 75 | +echo ======================================== | ||
| 76 | +echo 编译完成! | ||
| 77 | +echo ======================================== | ||
| 78 | +echo. | ||
| 79 | +echo 验证 CLI 安装: | ||
| 80 | +cd "%PROJECT_ROOT%\cli" | ||
| 81 | +.venv\Scripts\python.exe -c "from openjiuwen_runtime.management.sdk import DeploymentManager; import inspect; print('SDK OK:', inspect.signature(DeploymentManager.deploy_agent))" 2>nul | ||
| 82 | +if errorlevel 1 ( | ||
| 83 | + echo ! CLI 验证失败 | ||
| 84 | +) | ||
| 85 | +echo. | ||
| 86 | +echo 验证 Server 安装: | ||
| 87 | +cd "%PROJECT_ROOT%\server" | ||
| 88 | +if exist ".venv\Scripts\python.exe" ( | ||
| 89 | + .venv\Scripts\python.exe -c "from openjiuwen_runtime.management.sdk import DeploymentManager; import inspect; src=inspect.getsource(DeploymentManager.deploy_agent); print('SDK OK') if '\"name\"' in src and 'return' in src else print('SDK FAIL: missing name in return')" 2>nul | ||
| 90 | + if errorlevel 1 ( | ||
| 91 | + echo ! Server 验证失败 | ||
| 92 | + ) | ||
| 93 | +) else ( | ||
| 94 | + echo ! Server venv 不存在,跳过验证 | ||
| 95 | +) | ||
| 96 | +echo. | ||
| 97 | +echo ======================================== | ||
| 98 | +echo 注意: 如果 Server 正在运行,请重启 Server 以加载新代码 | ||
| 99 | +echo ======================================== | ||
| 100 | +pause | ||
| @@ -0,0 +1,98 @@ | |||
| 1 | +#!/bin/bash | ||
| 2 | +# ======================================== | ||
| 3 | +# Agent Runtime Manager - Rebuild Script | ||
| 4 | +# ======================================== | ||
| 5 | +# 彻底清除缓存并重新编译 CLI 和 Server,解决 editable install 缓存问题 | ||
| 6 | + | ||
| 7 | +set -e | ||
| 8 | + | ||
| 9 | +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| 10 | +PROJECT_ROOT="$SCRIPT_DIR/.." | ||
| 11 | + | ||
| 12 | +echo "========================================" | ||
| 13 | +echo "开始清理缓存..." | ||
| 14 | +echo "========================================" | ||
| 15 | + | ||
| 16 | +# 1. 清除 CLI venv 中的缓存 | ||
| 17 | +echo "[1/7] 清除 CLI venv 缓存..." | ||
| 18 | +cd "$PROJECT_ROOT/cli" | ||
| 19 | +if [ -d ".venv/lib/python*/site-packages/openjiuwen_runtime" ]; then | ||
| 20 | + rm -rf .venv/lib/python*/site-packages/openjiuwen_runtime | ||
| 21 | + echo " - 已删除 CLI venv 中的 openjiuwen_runtime" | ||
| 22 | +fi | ||
| 23 | +find .venv -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true | ||
| 24 | +find .venv -type f -name "*.pyc" -delete 2>/dev/null || true | ||
| 25 | + | ||
| 26 | +# 2. 清除 Server venv 中的缓存 | ||
| 27 | +echo "[2/7] 清除 Server venv 缓存..." | ||
| 28 | +cd "$PROJECT_ROOT/server" | ||
| 29 | +if [ -d ".venv/lib/python*/site-packages/openjiuwen_runtime" ]; then | ||
| 30 | + rm -rf .venv/lib/python*/site-packages/openjiuwen_runtime | ||
| 31 | + echo " - 已删除 Server venv 中的 openjiuwen_runtime" | ||
| 32 | +fi | ||
| 33 | +find .venv -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true | ||
| 34 | +find .venv -type f -name "*.pyc" -delete 2>/dev/null || true | ||
| 35 | + | ||
| 36 | +# 3. 清除 SDK 源码中的 pyc 缓存 | ||
| 37 | +echo "[3/7] 清除 SDK 源码缓存..." | ||
| 38 | +cd "$PROJECT_ROOT/sdk" | ||
| 39 | +find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true | ||
| 40 | +find . -type f -name "*.pyc" -delete 2>/dev/null || true | ||
| 41 | + | ||
| 42 | +# 4. 卸载旧版本 SDK (CLI) | ||
| 43 | +echo "[4/7] 卸载 CLI 旧版本包..." | ||
| 44 | +cd "$PROJECT_ROOT/cli" | ||
| 45 | +uv pip uninstall openjiuwen-runtime-management-sdk 2>/dev/null || true | ||
| 46 | +uv pip uninstall agent-runtime-manager-cli 2>/dev/null || true | ||
| 47 | + | ||
| 48 | +# 5. 重新安装 SDK (CLI) | ||
| 49 | +echo "[5/7] 安装 SDK 到 CLI (editable mode)..." | ||
| 50 | +uv pip install -e "$PROJECT_ROOT/sdk" | ||
| 51 | + | ||
| 52 | +# 6. 重新安装 CLI | ||
| 53 | +echo "[6/7] 安装 CLI..." | ||
| 54 | +uv pip install -e ./ | ||
| 55 | + | ||
| 56 | +# 7. 重新安装 SDK (Server) - 使用 --python 指定Server的venv | ||
| 57 | +echo "[7/7] 安装 SDK 到 Server (editable mode)..." | ||
| 58 | +cd "$PROJECT_ROOT/server" | ||
| 59 | +if [ -f ".venv/bin/python" ]; then | ||
| 60 | + uv pip install --python .venv/bin/python -e "$PROJECT_ROOT/sdk" | ||
| 61 | +else | ||
| 62 | + echo " ! Server venv 不存在,跳过" | ||
| 63 | +fi | ||
| 64 | + | ||
| 65 | +echo "========================================" | ||
| 66 | +echo "编译完成!" | ||
| 67 | +echo "========================================" | ||
| 68 | +echo "" | ||
| 69 | +echo "验证 CLI 安装:" | ||
| 70 | +cd "$PROJECT_ROOT/cli" | ||
| 71 | +if .venv/bin/python -c "from openjiuwen_runtime.management.sdk import DeploymentManager; import inspect; print('SDK OK:', inspect.signature(DeploymentManager.deploy_agent))" 2>/dev/null; then | ||
| 72 | + : | ||
| 73 | +else | ||
| 74 | + echo " ! CLI 验证失败" | ||
| 75 | +fi | ||
| 76 | + | ||
| 77 | +echo "" | ||
| 78 | +echo "验证 Server 安装:" | ||
| 79 | +cd "$PROJECT_ROOT/server" | ||
| 80 | +if [ -f ".venv/bin/python" ]; then | ||
| 81 | + if .venv/bin/python -c " | ||
| 82 | +from openjiuwen_runtime.management.sdk import DeploymentManager | ||
| 83 | +import inspect | ||
| 84 | +src = inspect.getsource(DeploymentManager.deploy_agent) | ||
| 85 | +print('SDK OK') if '\"name\"' in src and 'return' in src else print('SDK FAIL: missing name in return') | ||
| 86 | +" 2>/dev/null; then | ||
| 87 | + : | ||
| 88 | + else | ||
| 89 | + echo " ! Server 验证失败" | ||
| 90 | + fi | ||
| 91 | +else | ||
| 92 | + echo " ! Server venv 不存在,跳过验证" | ||
| 93 | +fi | ||
| 94 | + | ||
| 95 | +echo "" | ||
| 96 | +echo "========================================" | ||
| 97 | +echo "注意: 如果 Server 正在运行,请重启 Server 以加载新代码" | ||
| 98 | +echo "========================================" | ||
| @@ -0,0 +1 @@ | |||
| 1 | +"""Agent Runtime Manager CLI""" | ||
| @@ -0,0 +1,251 @@ | |||
| 1 | +"""Agent Runtime Manager CLI v2.0 | ||
| 2 | + | ||
| 3 | +使用WHL包部署Agent和Plugin,支持虚拟环境隔离。 | ||
| 4 | +""" | ||
| 5 | + | ||
| 6 | +import asyncio | ||
| 7 | +import json | ||
| 8 | +import logging | ||
| 9 | +import sys | ||
| 10 | +from pathlib import Path | ||
| 11 | + | ||
| 12 | +import click | ||
| 13 | +from dotenv import load_dotenv | ||
| 14 | + | ||
| 15 | +from openjiuwen_runtime.management.sdk import ( | ||
| 16 | + DeploymentManager, | ||
| 17 | + DeploymentType, | ||
| 18 | + DeploymentStatus, | ||
| 19 | +) | ||
| 20 | + | ||
| 21 | +# 配置日志输出 | ||
| 22 | +logging.basicConfig( | ||
| 23 | + level=logging.INFO, | ||
| 24 | + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', | ||
| 25 | + datefmt='%Y-%m-%d %H:%M:%S' | ||
| 26 | +) | ||
| 27 | + | ||
| 28 | +# 加载.env配置文件(固定位置:manager根目录) | ||
| 29 | +_manager_root = Path(__file__).parent.parent.parent | ||
| 30 | +_env_file = _manager_root / ".env" | ||
| 31 | +load_dotenv(_env_file) | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + | ||
| 36 | +def cli(ctx): | ||
| 37 | + """Agent Runtime Manager CLI v2.0""" | ||
| 38 | + ctx.ensure_object(dict) | ||
| 39 | + | ||
| 40 | + # 从环境变量读取配置 | ||
| 41 | + ctx.obj["manager"] = DeploymentManager() | ||
| 42 | + | ||
| 43 | + | ||
| 44 | +# ==================== Agent 命令 ==================== | ||
| 45 | + | ||
| 46 | + | ||
| 47 | +def agent(): | ||
| 48 | + """Agent 管理""" | ||
| 49 | + pass | ||
| 50 | + | ||
| 51 | + | ||
| 52 | + | ||
| 53 | + | ||
| 54 | + | ||
| 55 | + | ||
| 56 | + | ||
| 57 | +def deploy(ctx, python_file_path, name, port): | ||
| 58 | + """部署 Agent(使用 Python 文件) | ||
| 59 | + | ||
| 60 | + \b | ||
| 61 | + Manager SDK 内部自动将 Python 文件打包为 WHL 包。 | ||
| 62 | + name 参数既是部署名称,也是打包的包名。 | ||
| 63 | + | ||
| 64 | + 示例: | ||
| 65 | + agent-runtime agent deploy ./my_agent.py --name my_agent | ||
| 66 | + agent-runtime agent deploy ./my_agent.py --name my_agent --port 8090 | ||
| 67 | + """ | ||
| 68 | + manager = ctx.obj["manager"] | ||
| 69 | + | ||
| 70 | + async def _deploy(): | ||
| 71 | + result = await manager.deploy_agent( | ||
| 72 | + python_file_path=python_file_path, | ||
| 73 | + name=name, | ||
| 74 | + port=port, | ||
| 75 | + ) | ||
| 76 | + click.echo(json.dumps(result, indent=2, ensure_ascii=False)) | ||
| 77 | + | ||
| 78 | + asyncio.run(_deploy()) | ||
| 79 | + | ||
| 80 | + | ||
| 81 | + | ||
| 82 | + | ||
| 83 | + | ||
| 84 | +def list(ctx, status): | ||
| 85 | + """查询 Agent 列表""" | ||
| 86 | + manager = ctx.obj["manager"] | ||
| 87 | + | ||
| 88 | + async def _list(): | ||
| 89 | + deployments = await manager.list_deployments( | ||
| 90 | + deployment_type=DeploymentType.AGENT, | ||
| 91 | + status=DeploymentStatus(status) if status else None, | ||
| 92 | + ) | ||
| 93 | + | ||
| 94 | + if not deployments: | ||
| 95 | + click.echo("No deployments found") | ||
| 96 | + return | ||
| 97 | + | ||
| 98 | + # 显示简略信息 | ||
| 99 | + click.echo(f"{'ID':<30} {'Name':<20} {'Status':<10} {'Port':<6} {'Package':<20} {'URL'}") | ||
| 100 | + click.echo("-" * 120) | ||
| 101 | + for dep in deployments: | ||
| 102 | + name = dep.get('name') or '-' | ||
| 103 | + dep_status = dep['status'].value if hasattr(dep['status'], 'value') else str(dep['status']) | ||
| 104 | + package_name = dep.get('package_name') or '-' | ||
| 105 | + url = dep.get('url') or '-' | ||
| 106 | + click.echo(f"{dep['deployment_id']:<30} {name:<20} {dep_status:<10} {dep['port']:<6} {package_name:<20} {url:<30}") | ||
| 107 | + | ||
| 108 | + asyncio.run(_list()) | ||
| 109 | + | ||
| 110 | + | ||
| 111 | + | ||
| 112 | + | ||
| 113 | + | ||
| 114 | +def get(ctx, deployment_id): | ||
| 115 | + """获取 Agent 详情""" | ||
| 116 | + manager = ctx.obj["manager"] | ||
| 117 | + | ||
| 118 | + async def _get(): | ||
| 119 | + deployment = await manager.get_deployment(deployment_id) | ||
| 120 | + if deployment: | ||
| 121 | + click.echo(json.dumps(deployment, indent=2, ensure_ascii=False)) | ||
| 122 | + else: | ||
| 123 | + click.echo(f"Deployment {deployment_id} not found", err=True) | ||
| 124 | + sys.exit(1) | ||
| 125 | + | ||
| 126 | + asyncio.run(_get()) | ||
| 127 | + | ||
| 128 | + | ||
| 129 | + | ||
| 130 | + | ||
| 131 | + | ||
| 132 | +def delete(ctx, deployment_id): | ||
| 133 | + """删除 Agent(会自动清理虚拟环境)""" | ||
| 134 | + manager = ctx.obj["manager"] | ||
| 135 | + | ||
| 136 | + async def _delete(): | ||
| 137 | + success = await manager.delete_deployment(deployment_id) | ||
| 138 | + if success: | ||
| 139 | + click.echo(f"Deployment {deployment_id} deleted") | ||
| 140 | + else: | ||
| 141 | + click.echo(f"Deployment {deployment_id} not found", err=True) | ||
| 142 | + sys.exit(1) | ||
| 143 | + | ||
| 144 | + asyncio.run(_delete()) | ||
| 145 | + | ||
| 146 | + | ||
| 147 | +# ==================== Plugin 命令 ==================== | ||
| 148 | + | ||
| 149 | + | ||
| 150 | +def plugin(): | ||
| 151 | + """Plugin 管理""" | ||
| 152 | + pass | ||
| 153 | + | ||
| 154 | + | ||
| 155 | + | ||
| 156 | + | ||
| 157 | + | ||
| 158 | + | ||
| 159 | + | ||
| 160 | +def deploy(ctx, python_file_path, name, port): | ||
| 161 | + """部署 Plugin(使用 Python 文件) | ||
| 162 | + | ||
| 163 | + \b | ||
| 164 | + Manager SDK 内部自动将 Python 文件打包为 WHL 包。 | ||
| 165 | + name 参数既是部署名称,也是打包的包名。 | ||
| 166 | + | ||
| 167 | + 示例: | ||
| 168 | + agent-runtime plugin deploy ./my_plugin.py --name my_plugin | ||
| 169 | + agent-runtime plugin deploy ./my_plugin.py --name my_plugin --port 8091 | ||
| 170 | + """ | ||
| 171 | + manager = ctx.obj["manager"] | ||
| 172 | + | ||
| 173 | + async def _deploy(): | ||
| 174 | + result = await manager.deploy_plugin( | ||
| 175 | + python_file_path=python_file_path, | ||
| 176 | + name=name, | ||
| 177 | + port=port, | ||
| 178 | + ) | ||
| 179 | + click.echo(json.dumps(result, indent=2, ensure_ascii=False)) | ||
| 180 | + | ||
| 181 | + asyncio.run(_deploy()) | ||
| 182 | + | ||
| 183 | + | ||
| 184 | + | ||
| 185 | + | ||
| 186 | + | ||
| 187 | +def list(ctx, status): | ||
| 188 | + """查询 Plugin 列表""" | ||
| 189 | + manager = ctx.obj["manager"] | ||
| 190 | + | ||
| 191 | + async def _list(): | ||
| 192 | + deployments = await manager.list_deployments( | ||
| 193 | + deployment_type=DeploymentType.PLUGIN, | ||
| 194 | + status=DeploymentStatus(status) if status else None, | ||
| 195 | + ) | ||
| 196 | + | ||
| 197 | + if not deployments: | ||
| 198 | + click.echo("No deployments found") | ||
| 199 | + return | ||
| 200 | + | ||
| 201 | + # 显示简略信息 | ||
| 202 | + click.echo(f"{'ID':<30} {'Name':<20} {'Status':<10} {'Port':<6} {'Package':<20} {'URL'}") | ||
| 203 | + click.echo("-" * 120) | ||
| 204 | + for dep in deployments: | ||
| 205 | + name = dep.get('name') or '-' | ||
| 206 | + dep_status = dep['status'].value if hasattr(dep['status'], 'value') else str(dep['status']) | ||
| 207 | + package_name = dep.get('package_name') or '-' | ||
| 208 | + url = dep.get('url') or '-' | ||
| 209 | + click.echo(f"{dep['deployment_id']:<30} {name:<20} {dep_status:<10} {dep['port']:<6} {package_name:<20} {url:<30}") | ||
| 210 | + | ||
| 211 | + asyncio.run(_list()) | ||
| 212 | + | ||
| 213 | + | ||
| 214 | + | ||
| 215 | + | ||
| 216 | + | ||
| 217 | +def get(ctx, deployment_id): | ||
| 218 | + """获取 Plugin 详情""" | ||
| 219 | + manager = ctx.obj["manager"] | ||
| 220 | + | ||
| 221 | + async def _get(): | ||
| 222 | + deployment = await manager.get_deployment(deployment_id) | ||
| 223 | + if deployment: | ||
| 224 | + click.echo(json.dumps(deployment, indent=2, ensure_ascii=False)) | ||
| 225 | + else: | ||
| 226 | + click.echo(f"Deployment {deployment_id} not found", err=True) | ||
| 227 | + sys.exit(1) | ||
| 228 | + | ||
| 229 | + asyncio.run(_get()) | ||
| 230 | + | ||
| 231 | + | ||
| 232 | + | ||
| 233 | + | ||
| 234 | + | ||
| 235 | +def delete(ctx, deployment_id): | ||
| 236 | + """删除 Plugin(会自动清理虚拟环境)""" | ||
| 237 | + manager = ctx.obj["manager"] | ||
| 238 | + | ||
| 239 | + async def _delete(): | ||
| 240 | + success = await manager.delete_deployment(deployment_id) | ||
| 241 | + if success: | ||
| 242 | + click.echo(f"Deployment {deployment_id} deleted") | ||
| 243 | + else: | ||
| 244 | + click.echo(f"Deployment {deployment_id} not found", err=True) | ||
| 245 | + sys.exit(1) | ||
| 246 | + | ||
| 247 | + asyncio.run(_delete()) | ||
| 248 | + | ||
| 249 | + | ||
| 250 | +if __name__ == "__main__": | ||
| 251 | + cli(obj={}) | ||
| @@ -0,0 +1,2 @@ | |||
| 1 | +fastapi>=0.100.0 | ||
| 2 | +uvicorn>=0.23.0 | ||
| @@ -0,0 +1,49 @@ | |||
| 1 | +"""Simple FastAPI Agent Example | ||
| 2 | + | ||
| 3 | +A standalone Python file that can be deployed directly. | ||
| 4 | +""" | ||
| 5 | + | ||
| 6 | +import argparse | ||
| 7 | +import uvicorn | ||
| 8 | +from fastapi import FastAPI | ||
| 9 | +from fastapi.responses import JSONResponse | ||
| 10 | + | ||
| 11 | +# Create FastAPI app | ||
| 12 | +app = FastAPI(title="Simple Agent", version="1.0.0") | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +async def root(): | ||
| 17 | + """Root endpoint""" | ||
| 18 | + return JSONResponse({ | ||
| 19 | + "message": "Hello World from simple_agent!", | ||
| 20 | + "status": "running" | ||
| 21 | + }) | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | +async def health(): | ||
| 26 | + """Health check""" | ||
| 27 | + return JSONResponse({ | ||
| 28 | + "status": "healthy" | ||
| 29 | + }) | ||
| 30 | + | ||
| 31 | + | ||
| 32 | +def main(): | ||
| 33 | + """Main entry point""" | ||
| 34 | + parser = argparse.ArgumentParser(description="Simple FastAPI Agent") | ||
| 35 | + parser.add_argument("--host", default="127.0.0.1", help="Host to bind to") | ||
| 36 | + parser.add_argument("--port", type=int, default=8090, help="Port to bind to") | ||
| 37 | + args = parser.parse_args() | ||
| 38 | + | ||
| 39 | + # Start uvicorn server | ||
| 40 | + uvicorn.run( | ||
| 41 | + app, | ||
| 42 | + host=args.host, | ||
| 43 | + port=args.port, | ||
| 44 | + log_level="info" | ||
| 45 | + ) | ||
| 46 | + | ||
| 47 | + | ||
| 48 | +if __name__ == "__main__": | ||
| 49 | + main() | ||
| @@ -0,0 +1,62 @@ | |||
| 1 | +"""Simple FastAPI Plugin Example | ||
| 2 | + | ||
| 3 | +A standalone Python file that can be deployed directly as a plugin. | ||
| 4 | +""" | ||
| 5 | + | ||
| 6 | +import argparse | ||
| 7 | +import uvicorn | ||
| 8 | +from fastapi import FastAPI | ||
| 9 | +from fastapi.responses import JSONResponse | ||
| 10 | + | ||
| 11 | +# Create FastAPI app | ||
| 12 | +app = FastAPI(title="Simple Plugin", version="1.0.0") | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +async def root(): | ||
| 17 | + """Root endpoint""" | ||
| 18 | + return JSONResponse({ | ||
| 19 | + "message": "Hello World from simple_plugin!", | ||
| 20 | + "type": "plugin", | ||
| 21 | + "status": "running" | ||
| 22 | + }) | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +async def list_tools(): | ||
| 27 | + """List available tools""" | ||
| 28 | + return JSONResponse({ | ||
| 29 | + "tools": [ | ||
| 30 | + {"name": "current_weather", "description": "Query current weather"}, | ||
| 31 | + {"name": "weather_forecast", "description": "Query weather forecast"} | ||
| 32 | + ] | ||
| 33 | + }) | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + | ||
| 37 | +async def call_tool(tool_name: str): | ||
| 38 | + """Call a tool""" | ||
| 39 | + return JSONResponse({ | ||
| 40 | + "tool": tool_name, | ||
| 41 | + "result": f"Executed {tool_name} successfully" | ||
| 42 | + }) | ||
| 43 | + | ||
| 44 | + | ||
| 45 | +def main(): | ||
| 46 | + """Main entry point""" | ||
| 47 | + parser = argparse.ArgumentParser(description="Simple FastAPI Plugin") | ||
| 48 | + parser.add_argument("--host", default="127.0.0.1", help="Host to bind to") | ||
| 49 | + parser.add_argument("--port", type=int, default=8091, help="Port to bind to") | ||
| 50 | + args = parser.parse_args() | ||
| 51 | + | ||
| 52 | + # Start uvicorn server | ||
| 53 | + uvicorn.run( | ||
| 54 | + app, | ||
| 55 | + host=args.host, | ||
| 56 | + port=args.port, | ||
| 57 | + log_level="info" | ||
| 58 | + ) | ||
| 59 | + | ||
| 60 | + | ||
| 61 | +if __name__ == "__main__": | ||
| 62 | + main() | ||
| @@ -0,0 +1,35 @@ | |||
| 1 | +"""Mock Agent for testing""" | ||
| 2 | + | ||
| 3 | +import asyncio | ||
| 4 | +from fastapi import FastAPI | ||
| 5 | +from uvicorn import Config, Server | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +app = FastAPI(title="TestAgent") | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | +async def root(): | ||
| 13 | + return {"message": "Hello World"} | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +async def health(): | ||
| 18 | + return {"status": "healthy"} | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +if __name__ == "__main__": | ||
| 22 | + import argparse | ||
| 23 | + import sys | ||
| 24 | + | ||
| 25 | + parser = argparse.ArgumentParser() | ||
| 26 | + parser.add_argument("--host", default="127.0.0.1") | ||
| 27 | + parser.add_argument("--port", type=int, default=8090) | ||
| 28 | + args = parser.parse_args() | ||
| 29 | + | ||
| 30 | + # 使用 uvicorn 运行 | ||
| 31 | + config = Config(app=app, host=args.host, port=args.port, log_level="info") | ||
| 32 | + server = Server(config) | ||
| 33 | + | ||
| 34 | + print(f"Starting TestAgent on http://{args.host}:{args.port}") | ||
| 35 | + asyncio.run(server.serve()) | ||
| @@ -0,0 +1,45 @@ | |||
| 1 | +[build-system] | ||
| 2 | +requires = ["setuptools>=61.0", "wheel"] | ||
| 3 | +build-backend = "setuptools.build_meta" | ||
| 4 | + | ||
| 5 | +[project] | ||
| 6 | +name = "openjiuwen-runtime-management-sdk" | ||
| 7 | +version = "0.1.0" | ||
| 8 | +description = "OpenJiuwen Runtime" | ||
| 9 | +requires-python = ">=3.11.4" | ||
| 10 | +dependencies = [ | ||
| 11 | + "build>=1.0.0", | ||
| 12 | + "sqlalchemy>=2.0.0", | ||
| 13 | + "pymysql>=1.0.0", | ||
| 14 | + "pydantic>=2.0.0", | ||
| 15 | + "cryptography>=42.0.0", | ||
| 16 | +] | ||
| 17 | + | ||
| 18 | +[tool.uv] | ||
| 19 | +default-groups = ['dev'] | ||
| 20 | + | ||
| 21 | +[dependency-groups] | ||
| 22 | +dev = [ | ||
| 23 | + "coverage==7.8.0", | ||
| 24 | + "pytest==8.3.5", | ||
| 25 | + "pytest-asyncio==1.2.0", | ||
| 26 | + "pytest-mock==3.14.0", | ||
| 27 | + "ruff==0.9.10", | ||
| 28 | +] | ||
| 29 | + | ||
| 30 | +[[tool.uv.index]] | ||
| 31 | +url = "https://mirrors.aliyun.com/pypi/simple/" | ||
| 32 | + | ||
| 33 | +[tool.setuptools] | ||
| 34 | +packages = ["openjiuwen_runtime.management.sdk", | ||
| 35 | + "openjiuwen_runtime.management.sdk.models", | ||
| 36 | + "openjiuwen_runtime.management.sdk.database", | ||
| 37 | + "openjiuwen_runtime.management.sdk.deployer", | ||
| 38 | + "openjiuwen_runtime.management.sdk.utils"] | ||
| 39 | + | ||
| 40 | +[tool.setuptools.package-dir] | ||
| 41 | +"openjiuwen_runtime.management.sdk" = "src" | ||
| 42 | +"openjiuwen_runtime.management.sdk.models" = "src/models" | ||
| 43 | +"openjiuwen_runtime.management.sdk.database" = "src/database" | ||
| 44 | +"openjiuwen_runtime.management.sdk.deployer" = "src/deployer" | ||
| 45 | +"openjiuwen_runtime.management.sdk.utils" = "src/utils" | ||
| @@ -0,0 +1,54 @@ | |||
| 1 | +"""SDK使用示例 | ||
| 2 | + | ||
| 3 | +使用前请先初始化数据库: | ||
| 4 | + from openjiuwen_runtime.management.sdk import init_database | ||
| 5 | + init_database() | ||
| 6 | +""" | ||
| 7 | + | ||
| 8 | +import asyncio | ||
| 9 | +from pathlib import Path | ||
| 10 | +from dotenv import load_dotenv | ||
| 11 | + | ||
| 12 | +from openjiuwen_runtime.management.sdk import DeploymentManager | ||
| 13 | + | ||
| 14 | +# 加载.env配置文件(manager根目录) | ||
| 15 | +_manager_root = Path(__file__).parent.parent | ||
| 16 | +_env_file = _manager_root / ".env" | ||
| 17 | +load_dotenv(_env_file) | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +async def main(): | ||
| 21 | + """SDK使用示例""" | ||
| 22 | + | ||
| 23 | + # 创建部署管理器 | ||
| 24 | + manager = DeploymentManager() | ||
| 25 | + | ||
| 26 | + # 部署Agent | ||
| 27 | + print("Deploying agent...") | ||
| 28 | + result = await manager.deploy_agent( | ||
| 29 | + local_file_path=str(Path(__file__).parent / "mock_agent_app.py"), | ||
| 30 | + name="HelloAgent", | ||
| 31 | + port=9999, | ||
| 32 | + ) | ||
| 33 | + print(f" Deployment ID: {result['deployment_id']}") | ||
| 34 | + print(f" URL: {result['url']}") | ||
| 35 | + print(f" Status: {result['status']}") | ||
| 36 | + | ||
| 37 | + # 查询部署列表 | ||
| 38 | + print("\nListing agents...") | ||
| 39 | + deployments = await manager.list_deployments() | ||
| 40 | + for dep in deployments: | ||
| 41 | + print(f" - {dep['deployment_id']}: {dep['status']}") | ||
| 42 | + | ||
| 43 | + # 获取部署详情 | ||
| 44 | + deployment = await manager.get_deployment(result['deployment_id']) | ||
| 45 | + print(f"\nDeployment details: {deployment['status']}") | ||
| 46 | + | ||
| 47 | + # 删除部署 | ||
| 48 | + print("\nDeleting deployment...") | ||
| 49 | + await manager.delete_deployment(result['deployment_id']) | ||
| 50 | + print(" Deleted!") | ||
| 51 | + | ||
| 52 | + | ||
| 53 | +if __name__ == "__main__": | ||
| 54 | + asyncio.run(main()) | ||
| @@ -0,0 +1,17 @@ | |||
| 1 | +"""枚举定义""" | ||
| 2 | + | ||
| 3 | +from enum import Enum | ||
| 4 | + | ||
| 5 | + | ||
| 6 | +class DeploymentType(str, Enum): | ||
| 7 | + """部署类型""" | ||
| 8 | + AGENT = "agent" | ||
| 9 | + PLUGIN = "plugin" | ||
| 10 | + | ||
| 11 | + | ||
| 12 | +class DeploymentStatus(str, Enum): | ||
| 13 | + """部署状态""" | ||
| 14 | + PENDING = "pending" | ||
| 15 | + RUNNING = "running" | ||
| 16 | + STOPPED = "stopped" | ||
| 17 | + FAILED = "failed" | ||
| @@ -0,0 +1,34 @@ | |||
| 1 | +[build-system] | ||
| 2 | +requires = ["setuptools>=61.0", "wheel"] | ||
| 3 | +build-backend = "setuptools.build_meta" | ||
| 4 | + | ||
| 5 | +[project] | ||
| 6 | +name = "agent-runtime-manager-server" | ||
| 7 | +version = "1.0.0" | ||
| 8 | +description = "Agent Runtime Manager Server" | ||
| 9 | +requires-python = ">=3.11.4" | ||
| 10 | +dependencies = [ | ||
| 11 | + "fastapi>=0.100.0", | ||
| 12 | + "uvicorn>=0.23.0", | ||
| 13 | + "python-dotenv>=1.0.0", | ||
| 14 | + "python-multipart>=0.0.6", | ||
| 15 | + # SDK dependency (local path) | ||
| 16 | + "openjiuwen-runtime-management-sdk @ file:///${PROJECT_ROOT}/../sdk", | ||
| 17 | +] | ||
| 18 | + | ||
| 19 | +[tool.uv] | ||
| 20 | +default-groups = ['dev'] | ||
| 21 | + | ||
| 22 | +[dependency-groups] | ||
| 23 | +dev = [ | ||
| 24 | + "ruff==0.9.10", | ||
| 25 | +] | ||
| 26 | + | ||
| 27 | +[[tool.uv.index]] | ||
| 28 | +url = "https://mirrors.aliyun.com/pypi/simple/" | ||
| 29 | + | ||
| 30 | +[tool.setuptools] | ||
| 31 | +packages = ["agent_runtime_manager.server"] | ||
| 32 | + | ||
| 33 | +[tool.setuptools.package-dir] | ||
| 34 | +"agent_runtime_manager.server" = "src" | ||
| @@ -0,0 +1,27 @@ | |||
| 1 | +"""Server Configuration""" | ||
| 2 | + | ||
| 3 | +import os | ||
| 4 | +from dotenv import load_dotenv | ||
| 5 | + | ||
| 6 | +# 加载 .env 配置 | ||
| 7 | +load_dotenv() | ||
| 8 | + | ||
| 9 | + | ||
| 10 | +class Settings: | ||
| 11 | + """服务器配置""" | ||
| 12 | + | ||
| 13 | + # MySQL | ||
| 14 | + MYSQL_HOST: str = os.getenv("DB_HOST", "localhost") | ||
| 15 | + MYSQL_PORT: int = int(os.getenv("DB_PORT", "3306")) | ||
| 16 | + MYSQL_DATABASE: str = os.getenv("DB_NAME", "agent_runtime") | ||
| 17 | + MYSQL_USER: str = os.getenv("DB_USER", "root") | ||
| 18 | + MYSQL_PASSWORD: str = os.getenv("DB_PASSWORD", "") | ||
| 19 | + | ||
| 20 | + # OSS | ||
| 21 | + OSS_ENDPOINT: str = os.getenv("OSS_ENDPOINT", "") | ||
| 22 | + OSS_BUCKET: str = os.getenv("OSS_BUCKET", "") | ||
| 23 | + OSS_ACCESS_KEY: str = os.getenv("OSS_ACCESS_KEY", "") | ||
| 24 | + OSS_SECRET_KEY: str = os.getenv("OSS_SECRET_KEY", "") | ||
| 25 | + | ||
| 26 | + | ||
| 27 | +settings = Settings() | ||