Files
zk-data-agent/agent_platform/bootstrap.py
T
2026-07-26 17:40:51 +08:00

174 lines
5.9 KiB
Python

from __future__ import annotations
import asyncio
import os
import sys
from typing import Any
import httpx
from agent_platform.models import MODEL_SPECS
def _required(name: str) -> str:
value = os.getenv(name, "").strip()
if not value:
raise RuntimeError(f"{name} is required")
return value
async def _wait_until_ready(client: httpx.AsyncClient, base_url: str) -> None:
for _ in range(120):
try:
response = await client.get(f"{base_url}/health")
if response.status_code == 200:
return
except httpx.HTTPError:
pass
await asyncio.sleep(2)
raise RuntimeError("Open WebUI did not become healthy")
def _model_payload() -> list[dict[str, Any]]:
public_read = [{"principal_type": "user", "principal_id": "*", "permission": "read"}]
items = []
for spec in MODEL_SPECS.values():
is_chat = spec.mode == "chat"
items.append(
{
"id": spec.public_id,
"base_model_id": None,
"name": spec.display_name,
"params": {"function_calling": "native"} if is_chat else {},
"meta": {
"description": (
"快速问答,使用原生工具循环。" if is_chat else "长链路工作,使用 K1412 Work Agent。"
),
"toolIds": ["server:workspace"] if is_chat else [],
"capabilities": {
"tool_calling": is_chat,
"builtin_tools": False,
"file_context": False,
"citations": False,
"vision": False,
"file_upload": False,
},
"tags": [{"name": "Chat" if is_chat else "Work"}],
},
"access_grants": public_read,
"is_active": True,
}
)
return items
async def bootstrap() -> None:
base_url = os.getenv("OPENWEBUI_INTERNAL_URL", "http://web:8080").rstrip("/")
admin_email = _required("WEBUI_ADMIN_EMAIL")
admin_password = _required("WEBUI_ADMIN_PASSWORD")
gateway_key = _required("INTERNAL_GATEWAY_KEY")
gateway_url = os.getenv("GATEWAY_URL", "http://gateway:8001").rstrip("/")
async with httpx.AsyncClient(timeout=30) as client:
await _wait_until_ready(client, base_url)
response = await client.post(
f"{base_url}/api/v1/auths/signin",
json={"email": admin_email, "password": admin_password},
)
response.raise_for_status()
token = response.json().get("token")
if not token:
raise RuntimeError("Open WebUI sign-in returned no token")
headers = {"Authorization": f"Bearer {token}"}
config_response = await client.get(f"{base_url}/api/v1/auths/admin/config", headers=headers)
config_response.raise_for_status()
config = config_response.json()
config.update(
{
"ENABLE_SIGNUP": True,
"DEFAULT_USER_ROLE": "pending",
"ENABLE_API_KEYS": False,
"ENABLE_COMMUNITY_SHARING": False,
"ENABLE_MESSAGE_RATING": False,
"ENABLE_FOLDERS": False,
"ENABLE_AUTOMATIONS": False,
"ENABLE_CHANNELS": False,
"ENABLE_CALENDAR": False,
"ENABLE_MEMORIES": False,
"ENABLE_NOTES": False,
"ENABLE_USER_WEBHOOKS": False,
}
)
update_response = await client.post(
f"{base_url}/api/v1/auths/admin/config",
headers=headers,
json=config,
)
update_response.raise_for_status()
evaluation_response = await client.post(
f"{base_url}/api/v1/evaluations/config",
headers=headers,
json={
"ENABLE_EVALUATION_ARENA_MODELS": False,
"EVALUATION_ARENA_MODELS": [],
},
)
evaluation_response.raise_for_status()
tool_server_response = await client.post(
f"{base_url}/api/v1/configs/tool_servers",
headers=headers,
json={
"TOOL_SERVER_CONNECTIONS": [
{
"url": gateway_url,
"path": "openapi.json",
"type": "openapi",
"auth_type": "bearer",
"headers": None,
"key": gateway_key,
"config": {
"enable": True,
"access_grants": [
{
"principal_type": "user",
"principal_id": "*",
"permission": "read",
}
],
},
"info": {
"id": "workspace",
"name": "Workspace",
"description": "当前用户的隔离编码工作区",
},
"spec_type": "url",
}
]
},
)
tool_server_response.raise_for_status()
model_response = await client.post(
f"{base_url}/api/v1/models/import",
headers=headers,
json={"models": _model_payload()},
)
model_response.raise_for_status()
print("Open WebUI bootstrap complete: auth policy, workspace tools, and eight fixed Agent entries are ready.")
def run() -> None:
try:
asyncio.run(bootstrap())
except Exception as exc:
print(f"Open WebUI bootstrap failed: {exc}", file=sys.stderr)
raise SystemExit(1) from exc
if __name__ == "__main__":
run()