feat: rebuild as multi-user web agent
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
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 six 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()
|
||||
Reference in New Issue
Block a user