feat: 添加MinIO文件系统支持并优化外部连接器功能
- 添加MinIO用户文件系统配置选项(BEAVER_MINIO_ROOT_USER等) - 更新外部连接器配置结构,包括BASE_URL和认证令牌设置 - 改进connector provider支持更多类型(official, feishu_bot等) - 实现Mistral模型推理模式支持reasoning_effort参数 - 增强外部连接器策略配置和运行时配置管理 - 添加connector bridge事件验证和安全保护机制 - 优化任务路由逻辑,区分simple_chat和new_task场景 - 更新初始技能工具提示配置,分离authoring admin功能
This commit is contained in:
@ -16,6 +16,11 @@ APP_INSTANCE_API_BASE=
|
||||
DEFAULT_AUTHZ_BASE_URL=http://beaver-authz-service:19090
|
||||
DEFAULT_AUTHZ_OUTLOOK_MCP_URL=
|
||||
DEFAULT_OUTLOOK_MCP_SERVER_ID=outlook_mcp
|
||||
DEFAULT_USER_FILES_MAX_UPLOAD_BYTES=5368709120
|
||||
DEFAULT_EXTERNAL_CONNECTOR_BASE_URL=http://external-connector:8787
|
||||
DEFAULT_EXTERNAL_CONNECTOR_TOKEN=
|
||||
DEFAULT_BEAVER_BRIDGE_TOKEN=
|
||||
DEFAULT_INITIAL_SKILLS_DIR=/home/ivan/xuan/beaver_project/skills
|
||||
|
||||
DEPLOY_PUBLIC_SCHEME=http
|
||||
DEPLOY_PUBLIC_BASE_DOMAIN=localhost
|
||||
|
||||
@ -60,8 +60,9 @@ uv run server.py
|
||||
- Docker socket:`/var/run/docker.sock`
|
||||
- `/home/ivan/xuan/beaver_project/app-instance`
|
||||
- `/home/ivan/xuan/beaver_project/router-proxy`
|
||||
- `/home/ivan/xuan/beaver_project/skills`
|
||||
|
||||
并传入对应环境变量,让容器内脚本路径仍能访问这两个目录。
|
||||
并传入对应环境变量,让容器内脚本路径仍能访问这些目录。
|
||||
|
||||
关键点:
|
||||
|
||||
@ -79,14 +80,16 @@ docker run -d \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v /home/ivan/xuan/beaver_project/app-instance:/home/ivan/xuan/beaver_project/app-instance \
|
||||
-v /home/ivan/xuan/beaver_project/router-proxy:/home/ivan/xuan/beaver_project/router-proxy \
|
||||
-v /home/ivan/xuan/beaver_project/skills:/home/ivan/xuan/beaver_project/skills:ro \
|
||||
-e APP_INSTANCE_DIR=/home/ivan/xuan/beaver_project/app-instance \
|
||||
-e ROUTER_PROXY_DIR=/home/ivan/xuan/beaver_project/router-proxy \
|
||||
-e DEFAULT_INITIAL_SKILLS_DIR=/home/ivan/xuan/beaver_project/skills \
|
||||
-e DEPLOY_CONTROL_API_TOKEN=change-me \
|
||||
-e APP_INSTANCE_IMAGE=beaver/app-instance:latest \
|
||||
-e APP_INSTANCE_NETWORK_NAME=beaver-instance-edge \
|
||||
beaver/deploy-control:latest
|
||||
```
|
||||
|
||||
如果这里错把宿主机目录映射成容器内的另一个短路径,例如 `/app-instance`,那么 `deploy-control` 通过 Docker socket 创建实例时会把错误路径传给 Docker,最终导致实例容器拿不到 `config.json` 并持续重启。
|
||||
如果这里错把宿主机目录映射成容器内的另一个短路径,例如 `/app-instance`,那么 `deploy-control` 通过 Docker socket 创建实例时会把错误路径传给 Docker,最终导致实例容器拿不到 `config.json` 并持续重启。`skills` 目录也必须挂载到同一个绝对路径;否则新实例第一次创建时不会在 workspace 里种入初始 skills。
|
||||
|
||||
新实例注册时不会写入模型 provider/API key。注册后由 `auth-portal` 引导页调用 `POST /api/instances/configure-provider`,在用户确认后写入该实例配置并重启实例容器。
|
||||
|
||||
@ -42,6 +42,13 @@ DEFAULT_AUTHZ_INTERNAL_TOKEN = os.environ.get("DEFAULT_AUTHZ_INTERNAL_TOKEN", ""
|
||||
DEFAULT_AUTHZ_OUTLOOK_MCP_URL = os.environ.get("DEFAULT_AUTHZ_OUTLOOK_MCP_URL", "").strip()
|
||||
DEFAULT_OUTLOOK_MCP_SERVER_ID = os.environ.get("DEFAULT_OUTLOOK_MCP_SERVER_ID", "outlook_mcp").strip() or "outlook_mcp"
|
||||
DEFAULT_USER_FILES_MAX_UPLOAD_BYTES = os.environ.get("DEFAULT_USER_FILES_MAX_UPLOAD_BYTES", "").strip()
|
||||
DEFAULT_EXTERNAL_CONNECTOR_BASE_URL = os.environ.get(
|
||||
"DEFAULT_EXTERNAL_CONNECTOR_BASE_URL",
|
||||
"http://external-connector:8787",
|
||||
).strip()
|
||||
DEFAULT_EXTERNAL_CONNECTOR_TOKEN = os.environ.get("DEFAULT_EXTERNAL_CONNECTOR_TOKEN", "").strip()
|
||||
DEFAULT_BEAVER_BRIDGE_TOKEN = os.environ.get("DEFAULT_BEAVER_BRIDGE_TOKEN", "").strip()
|
||||
DEFAULT_INITIAL_SKILLS_DIR = os.environ.get("DEFAULT_INITIAL_SKILLS_DIR", str(APP_INSTANCE_DIR.parent / "skills")).strip()
|
||||
PUBLIC_SCHEME = os.environ.get("DEPLOY_PUBLIC_SCHEME", "http").strip() or "http"
|
||||
PUBLIC_BASE_DOMAIN = os.environ.get("DEPLOY_PUBLIC_BASE_DOMAIN", "localhost").strip()
|
||||
PUBLIC_HOST_TEMPLATE = os.environ.get("DEPLOY_PUBLIC_HOST_TEMPLATE", "{slug}.{base_domain}").strip()
|
||||
@ -274,6 +281,14 @@ def create_or_get_instance(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
command.extend(["--outlook-mcp-server-id", DEFAULT_OUTLOOK_MCP_SERVER_ID])
|
||||
if DEFAULT_USER_FILES_MAX_UPLOAD_BYTES:
|
||||
command.extend(["--user-files-max-upload-bytes", DEFAULT_USER_FILES_MAX_UPLOAD_BYTES])
|
||||
if DEFAULT_EXTERNAL_CONNECTOR_BASE_URL:
|
||||
command.extend(["--external-connector-base-url", DEFAULT_EXTERNAL_CONNECTOR_BASE_URL])
|
||||
if DEFAULT_EXTERNAL_CONNECTOR_TOKEN:
|
||||
command.extend(["--external-connector-token", DEFAULT_EXTERNAL_CONNECTOR_TOKEN])
|
||||
if DEFAULT_BEAVER_BRIDGE_TOKEN:
|
||||
command.extend(["--bridge-token", DEFAULT_BEAVER_BRIDGE_TOKEN])
|
||||
if DEFAULT_INITIAL_SKILLS_DIR:
|
||||
command.extend(["--initial-skills-dir", DEFAULT_INITIAL_SKILLS_DIR])
|
||||
if payload.get("replace") is True:
|
||||
command.append("--replace")
|
||||
|
||||
|
||||
58
deploy-control/tests/test_connector_instance_config.py
Normal file
58
deploy-control/tests/test_connector_instance_config.py
Normal file
@ -0,0 +1,58 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
SERVER_PATH = Path(__file__).resolve().parents[1] / "server.py"
|
||||
|
||||
|
||||
def _load_server_module():
|
||||
spec = importlib.util.spec_from_file_location("deploy_control_server_connector_tests", SERVER_PATH)
|
||||
assert spec and spec.loader
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def test_new_instance_receives_external_connector_configuration(monkeypatch) -> None:
|
||||
server = _load_server_module()
|
||||
commands: list[list[str]] = []
|
||||
record: dict[str, Any] = {
|
||||
"instance_id": "terminaltest",
|
||||
"container_name": "app-instance-terminaltest",
|
||||
"host_port": 20001,
|
||||
"public_url": "http://terminaltest.example.test",
|
||||
}
|
||||
lookups = iter([None, None, record])
|
||||
|
||||
monkeypatch.setattr(server, "get_registry_record", lambda **_kwargs: next(lookups))
|
||||
monkeypatch.setattr(server, "ensure_network", lambda: None)
|
||||
monkeypatch.setattr(server, "ensure_proxy", lambda: None)
|
||||
monkeypatch.setattr(server, "wait_for_backend", lambda _record: None)
|
||||
monkeypatch.setattr(server, "DEFAULT_EXTERNAL_CONNECTOR_BASE_URL", "http://external-connector:8787")
|
||||
monkeypatch.setattr(server, "DEFAULT_EXTERNAL_CONNECTOR_TOKEN", "connector-token")
|
||||
monkeypatch.setattr(server, "DEFAULT_BEAVER_BRIDGE_TOKEN", "bridge-token")
|
||||
monkeypatch.setattr(server, "DEFAULT_INITIAL_SKILLS_DIR", "/srv/beaver/skills")
|
||||
|
||||
def capture_command(args: list[str], **_kwargs: Any) -> str:
|
||||
commands.append(args)
|
||||
return ""
|
||||
|
||||
monkeypatch.setattr(server, "run_command", capture_command)
|
||||
|
||||
result = server.create_or_get_instance(
|
||||
{
|
||||
"username": "terminaltest",
|
||||
"password": "secret",
|
||||
"instance_id": "terminaltest",
|
||||
}
|
||||
)
|
||||
|
||||
command = commands[0]
|
||||
assert command[command.index("--external-connector-base-url") + 1] == "http://external-connector:8787"
|
||||
assert command[command.index("--external-connector-token") + 1] == "connector-token"
|
||||
assert command[command.index("--bridge-token") + 1] == "bridge-token"
|
||||
assert command[command.index("--initial-skills-dir") + 1] == "/srv/beaver/skills"
|
||||
assert result["created"] is True
|
||||
Reference in New Issue
Block a user