feat: 平台内部agent独立模块(从pipeline-service迁出)

能力包(5工具/管理员硬门禁/模型自动配置)+前端入口+宿主集成说明
This commit is contained in:
yumoqing 2026-09-02 15:26:16 +08:00
parent 38ed5726ce
commit 25e71b548b
9 changed files with 664 additions and 0 deletions

View File

@ -1,2 +1,30 @@
# pipeline-platform
产线平台内部 agent 模块(宿主不装则零接触)。
## 内容
- **能力包** `pipeline_platform/platform_ability.py`pipeline_id=`platform_general`
- platform_llm_status — 模型治理状态(供应商/账号余额/模型定价)
- fetch_model_doc — 抓取模型 API 文档SSRF 防护:仅公网域名)
- extract_llm_api_spec — LLM 通读文档提取配置规格(端点/协议/定价)
- apply_llm_config — 写入配置(幂等:供应商按名复用、模型按 (vendor, vendor_model_id) 只更新定价)
- platform_modules — 平台模块清单
- **前端入口** `wwwroot/agent_platform/index.ui`:主菜单「平台内部助手」(仅管理员可见)
- **技能**随 pipeline-core `skills_library/pipelines/platform_general/` 分发(平台架构地图 + 模型自动配置工作流)
## 权限
所有工具仅 `owner.superuser` / `owner.admin` 可用,**代码层硬门禁**(每个 handler 入口校验
RBAC 角色),不依赖 prompt。菜单入口同样 Jinja `{% if is_admin %}` 门控。
## 定价真实性规则
- 定价只允许来自文档原文,文档没写一律 null禁止编造
- 单位统一「元/千tokens」元/百万 ÷1000
- 非 openai_compat 协议的模板存档但运行时不可调,如实告知
## 宿主集成(已接入)
- `pipeline-app/build.sh`clone/pip install/wwwroot 软链列表已含本模块
- `pipeline-app/app/pipeline_app.py``load_pipeline_platform()`(在 load_pipeline_service 之后)

View File

@ -0,0 +1,14 @@
"""pipeline_platform — 产线平台内部 agent 模块(宿主不装则零接触)。
内部 agent = 平台自身的运维/管理会话能力
- 能力包platform_ability模型治理状态/模型自动配置通读 API 文档
供应商端点/适配模板/模型目录/定价/平台模块清单
- 前端入口wwwroot/agent_platformpipeline_id=platform_general 会话页
- 技能随 pipeline-core skills_library/pipelines/platform_general 分发
权限模型所有工具仅管理员owner.superuser / owner.admin可用代码层硬门禁
"""
__version__ = "0.1.0"
PIPELINE_ID = "platform_general"

Binary file not shown.

Binary file not shown.

27
pipeline_platform/init.py Normal file
View File

@ -0,0 +1,27 @@
"""pipeline-platform 模块装载入口。
宿主应用pipeline-app init() 中调用 load_pipeline_platform() 即挂载
能力包注册platform_abilityimport 即注册
未装本模块的宿主完全零接触 import 本包则以上全部不发生
"""
import logging
logger = logging.getLogger("pipeline.platform")
_loaded = False
def load_pipeline_platform():
"""挂载平台内部 agent幂等"""
global _loaded
if _loaded:
return True
try:
from . import platform_ability # noqa: F401import 即注册能力包)
logger.info("[pipeline_platform] ability registered: platform_general")
_loaded = True
return True
except Exception as e:
logger.warning("[pipeline_platform] ability 注册失败: %s", str(e)[:200])
return False

View File

@ -0,0 +1,521 @@
"""
pipeline-service: platform_ability 平台内部 agent 能力包pipeline_id=platform_general
产线平台自身的运维/管理 agent拥有平台应用和各模块的技能
核心能力是通读模型 API 文档 自动生成模型治理配置供应商/适配模板/模型/定价
权限模型代码层硬门禁不依赖 prompt
所有工具 handler 入口校验调用者的 RBAC 角色 {owner.superuser, owner.admin}
内部 agent 只服务管理员普通用户即使打开页面也调不动任何工具
配置生成链路用户给定 API 文档 完成配置
fetch_model_doc抓取文档页面SSRF 防护 http(s) 公网域名
extract_llm_api_specLLM 从文档提取端点/协议/请求响应格式/定价
apply_llm_config写库复用 pipeline-llm 模块的 CRUD 函数
llm_vendor.endpoints + llm_api_profile 模板 + llm_model 含四价
"""
import json
import logging
import re
from pipeline_core import (
ToolDefinition,
PipelineAbility,
register_ability,
)
logger = logging.getLogger("pipeline.platform_ability")
PLATFORM_PIPELINE_ID = "platform_general"
# 管理角色:内部 agent 仅管理员可用
ADMIN_ROLES = ("owner.superuser", "owner.admin")
# 当前运行时调用链支持的协议llm_bridge 固定 OpenAI 兼容路径)。
# 其他协议的适配模板会存入 llm_api_profile 备查,但运行时暂不渲染。
RUNTIME_PROTOCOLS = ("openai_compat",)
# ────────────────────── 权限门禁(代码层) ──────────────────────
async def _require_admin(sor, ctx) -> str:
"""校验当前用户是管理员。返回 ''=通过,否则错误信息。"""
uid = ctx.get("user_id", "") or ""
if not uid:
return "无法识别当前用户身份(未登录),内部 agent 仅管理员可用"
recs = await sor.sqlExe(
"SELECT r.orgtypeid, r.name FROM userrole ur JOIN role r ON ur.roleid=r.id "
"WHERE ur.userid=${u}$", {"u": uid})
await sor.sqlExe("COMMIT", {})
roles = set()
for r in (recs or []):
o = getattr(r, "orgtypeid", "") or ""
n = getattr(r, "name", "") or ""
if o and n:
roles.add("%s.%s" % (o, n))
if roles & set(ADMIN_ROLES):
return ""
return "权限不足:内部 agent 工具仅管理员(%s)可用,当前角色 %s" % (
"/".join(ADMIN_ROLES), sorted(roles) or "")
def _row(r):
"""sqlor row → dict容忍属性访问"""
if hasattr(r, "__dict__"):
return {k: v for k, v in r.__dict__.items() if not k.startswith("_")}
return {}
# ────────────────────── 工具 1模型治理状态 ──────────────────────
async def _h_platform_llm_status(sor, params, ctx):
"""供应商/账号/模型/用量概览(管理员诊断配置用)。"""
err = await _require_admin(sor, ctx)
if err:
return err
out = {}
for tbl in ("llm_vendor", "llm_account", "llm_model"):
recs = await sor.sqlExe(
"SELECT status, COUNT(*) AS c FROM " + tbl + " GROUP BY status", {})
await sor.sqlExe("COMMIT", {})
out[tbl] = {getattr(r, "status", ""): int(getattr(r, "c", 0)) for r in (recs or [])}
accs = await sor.sqlExe(
"SELECT name, balance, status FROM llm_account ORDER BY balance DESC LIMIT 20", {})
await sor.sqlExe("COMMIT", {})
out["accounts"] = [
{"name": getattr(r, "name", ""), "balance": float(getattr(r, "balance", 0) or 0),
"status": getattr(r, "status", "")} for r in (accs or [])]
models = await sor.sqlExe(
"SELECT name, vendor_model_id, capability, price_input, price_output, status "
"FROM llm_model ORDER BY created_at DESC LIMIT 30", {})
await sor.sqlExe("COMMIT", {})
out["models"] = [
{"name": getattr(r, "name", ""), "vendor_model_id": getattr(r, "vendor_model_id", ""),
"capability": getattr(r, "capability", ""),
"price_input": float(getattr(r, "price_input", 0) or 0),
"price_output": float(getattr(r, "price_output", 0) or 0),
"status": getattr(r, "status", "")} for r in (models or [])]
return json.dumps(out, ensure_ascii=False)
# ────────────────────── 工具 2抓取模型 API 文档 ──────────────────────
_MAX_DOC_CHARS = 60000
def _validate_doc_url(url: str) -> str:
"""SSRF 防护:仅允许 http(s) + 公网域名。返回 ''=通过,否则错误。"""
if not re.match(r"^https?://", url or ""):
return "文档 URL 必须是 http/https 链接"
host = re.sub(r"^https?://", "", url).split("/")[0].split(":")[0].lower()
if not re.match(r"^[a-z0-9][a-z0-9.-]+\.[a-z]{2,}$", host):
return "文档 URL 域名非法(不接受 IP/内网地址)"
if host in ("localhost",) or host.endswith((".local", ".internal", ".localhost")):
return "文档 URL 不允许指向内网/本地地址"
return ""
def _html_to_text(html: str) -> str:
"""粗暴去标签提取文本(文档页面用,不追求完美排版)。"""
txt = re.sub(r"(?is)<(script|style|noscript)[^>]*>.*?</\1>", " ", html or "")
txt = re.sub(r"(?is)<[^>]+>", " ", txt)
txt = re.sub(r"&nbsp;", " ", txt)
txt = re.sub(r"&lt;", "<", txt)
txt = re.sub(r"&gt;", ">", txt)
txt = re.sub(r"&amp;", "&", txt)
txt = re.sub(r"&quot;", '"', txt)
txt = re.sub(r"[ \t]+", " ", txt)
txt = re.sub(r"\n\s*\n+", "\n", txt)
return txt.strip()
async def _h_fetch_model_doc(sor, params, ctx):
"""抓取模型 API 文档页面 → 纯文本(供 LLM 提取配置规格)。"""
err = await _require_admin(sor, ctx)
if err:
return err
url = (params.get("url") or "").strip()
verr = _validate_doc_url(url)
if verr:
return verr
import aiohttp
try:
timeout = aiohttp.ClientTimeout(total=30)
async with aiohttp.ClientSession(timeout=timeout) as sess:
async with sess.get(url, headers={"User-Agent": "Mozilla/5.0"},
allow_redirects=True, ssl=False) as resp:
if resp.status != 200:
return "抓取失败HTTP %d" % resp.status
ctype = resp.headers.get("Content-Type", "")
raw = await resp.text(errors="replace")
except Exception as e:
return "抓取失败:%s" % str(e)[:200]
if "html" in ctype.lower() or raw.lstrip()[:15].lower().startswith(("<!doctype", "<html")):
text = _html_to_text(raw)
else:
text = raw
if len(text) > _MAX_DOC_CHARS:
text = text[:_MAX_DOC_CHARS] + "\n[文档过长已截断,共 %d 字符]" % len(text)
return text
# ────────────────────── 工具 3提取配置规格LLM ──────────────────────
_EXTRACT_PROMPT = """你是大模型 API 配置专家。通读下面这份模型 API 文档,提取配置规格。
只输出一个 JSON 对象不要 markdown 代码块字段
{
"vendor_name": "供应商名称",
"base_url": "API 基础地址(如 https://dashscope.aliyuncs.com/compatible-mode/v1",
"protocol": "openai_compat | dashscope_async | custom能走 /chat/completions 的填 openai_compat",
"endpoints": [{"base_url": "...", "region": "domestic|international", "timeout": 60}],
"chat_path": "对话接口路径(如 /chat/completions",
"auth_header": "认证头格式说明(如 Bearer API_KEY",
"request_fields": ["请求体字段名列表"],
"response_format": "响应格式说明content 字段路径 + usage 字段路径)",
"models": [{"vendor_model_id": "供应商侧模型ID", "capability": "t2t|t2i|i2t|t2v|embedding|rerank|tts|asr",
"price_input": 输入价元/千token或null, "price_output": 输出价元/千token或null,
"cost_input": 输入成本元/千token或null, "cost_output": 输出成本元/千token或null,
"description": "一句话说明"}],
"doc_notes": "文档中影响配置的关键注意点"
}
定价规则
- 文档给的单位若是/百万tokens换算为/千tokens= 原价/1000保留6位小数
- 文档没写价格的模型价格字段填 null禁止编造价格 description 注明
- cost_* 未知时与 price_* 相同无供应商折扣时成本=售价
文档内容
"""
async def _h_extract_llm_api_spec(sor, params, ctx):
"""LLM 通读文档文本 → 结构化配置规格JSON"""
err = await _require_admin(sor, ctx)
if err:
return err
doc_text = (params.get("doc_text") or "").strip()
if len(doc_text) < 100:
return "文档文本太短(<100字符无法提取配置——先用 fetch_model_doc 抓取"
try:
from pipeline_service.llm_bridge import llm_call_msgs
raw = await llm_call_msgs(
[{"role": "system", "content": _EXTRACT_PROMPT},
{"role": "user", "content": doc_text[:48000]}],
temperature=0.1, org_id="0", user_id=ctx.get("user_id", ""))
except Exception as e:
return "LLM 提取失败:%s" % str(e)[:200]
txt = (raw or "").strip()
if txt.startswith("```"):
txt = txt.split("\n", 1)[1] if "\n" in txt else txt
txt = txt.rsplit("```", 1)[0]
try:
spec = json.loads(txt)
except Exception:
m = re.search(r"\{.*\}", txt, re.S)
if not m:
return "LLM 输出不是合法 JSON%s" % txt[:300]
try:
spec = json.loads(m.group(0))
except Exception:
return "LLM 输出不是合法 JSON%s" % txt[:300]
return json.dumps(spec, ensure_ascii=False)
# ────────────────────── 工具 4写入配置幂等 ──────────────────────
def _f(v, default=0.0):
try:
return float(v)
except (TypeError, ValueError):
return default
async def _h_apply_llm_config(sor, params, ctx):
"""按提取的规格写库:供应商(复用/新建)+ 适配模板 + 模型(含定价)。
幂等供应商按名称复用模型按 (vendor, vendor_model_id) 复用
已存在则更新定价不重复创建
"""
err = await _require_admin(sor, ctx)
if err:
return err
try:
spec = json.loads(params.get("spec") or "{}")
except Exception:
return "spec 不是合法 JSON"
vendor_name = (spec.get("vendor_name") or "").strip()
if not vendor_name:
return "spec 缺少 vendor_name"
endpoints = spec.get("endpoints") or []
if not endpoints and spec.get("base_url"):
endpoints = [{"base_url": spec["base_url"], "region": "domestic", "timeout": 60}]
if not endpoints:
return "spec 缺少 endpoints/base_url"
protocol = (spec.get("protocol") or "openai_compat").strip() or "openai_compat"
models = spec.get("models") or []
if not models:
return "spec.models 为空——文档里没有可配置的模型?"
from appPublic.uniqueID import getID
# 1. 供应商:按名称复用
recs = await sor.sqlExe(
"SELECT id, endpoints FROM llm_vendor WHERE name=${n}$", {"n": vendor_name})
await sor.sqlExe("COMMIT", {})
if recs:
vendor_id = getattr(recs[0], "id", "")
eps_old = []
try:
eps_old = json.loads(getattr(recs[0], "endpoints", "") or "[]")
except Exception:
eps_old = []
merged = list(eps_old)
added_eps = []
for ep in endpoints:
bu = (ep.get("base_url") or "").rstrip("/")
if bu and bu not in [(e.get("base_url") or "").rstrip("/") for e in merged]:
merged.append({"base_url": bu, "region": ep.get("region") or "domestic",
"timeout": int(_f(ep.get("timeout"), 60))})
added_eps.append(bu)
await sor.sqlExe(
"UPDATE llm_vendor SET endpoints=${e}$, protocol=${p}$, updated_at=NOW() "
"WHERE id=${i}$",
{"e": json.dumps(merged, ensure_ascii=False), "p": protocol, "i": vendor_id})
await sor.sqlExe("COMMIT", {})
vendor_action = "复用供应商 %s(新增端点 %d 个)" % (vendor_id, len(added_eps))
else:
vendor_id = getID()
eps_norm = [{"base_url": (ep.get("base_url") or "").rstrip("/"),
"region": ep.get("region") or "domestic",
"timeout": int(_f(ep.get("timeout"), 60))} for ep in endpoints]
await sor.C("llm_vendor", {
"id": vendor_id, "name": vendor_name, "protocol": protocol,
"endpoints": json.dumps(eps_norm, ensure_ascii=False),
"description": spec.get("doc_notes", "") or "",
"status": "active", "org_id": "0"})
vendor_action = "新建供应商 %s%s" % (vendor_id, vendor_name)
# 2. 适配模板:按 (协议×能力) 复用,缺则新建
profile_ids = {}
caps = sorted(set((m.get("capability") or "t2t") for m in models))
for cap in caps:
recs = await sor.sqlExe(
"SELECT id FROM llm_api_profile WHERE protocol=${p}$ AND capability=${c}$ "
"AND status='active' ORDER BY created_at DESC LIMIT 1",
{"p": protocol, "c": cap})
await sor.sqlExe("COMMIT", {})
if recs:
profile_ids[cap] = getattr(recs[0], "id", "")
continue
req_tpl, resp_tpl = _default_templates(protocol, cap, spec)
pid = getID()
await sor.C("llm_api_profile", {
"id": pid, "name": "%s-%s-自动配置" % (protocol, cap),
"protocol": protocol, "capability": cap,
"request_template": req_tpl, "response_template": resp_tpl,
"param_schema": json.dumps([{"name": "prompt", "label": "提示词",
"uitype": "textarea", "required": True}],
ensure_ascii=False),
"status": "active"})
profile_ids[cap] = pid
# 3. 模型:按 (vendor, vendor_model_id) 幂等
created, updated, skipped = [], [], []
for m in models:
vmid = (m.get("vendor_model_id") or "").strip()
if not vmid:
skipped.append("(缺 vendor_model_id)")
continue
cap = m.get("capability") or "t2t"
price_in, price_out = _f(m.get("price_input")), _f(m.get("price_output"))
cost_in = _f(m.get("cost_input"), price_in)
cost_out = _f(m.get("cost_output"), price_out)
recs = await sor.sqlExe(
"SELECT id FROM llm_model WHERE vendor_id=${v}$ AND vendor_model_id=${m}$",
{"v": vendor_id, "m": vmid})
await sor.sqlExe("COMMIT", {})
if recs:
mid = getattr(recs[0], "id", "")
await sor.sqlExe(
"UPDATE llm_model SET price_input=${pi}$, price_output=${po}$, "
"cost_input=${ci}$, cost_output=${co}$, description=${d}$, updated_at=NOW() "
"WHERE id=${i}$",
{"pi": price_in, "po": price_out, "ci": cost_in, "co": cost_out,
"d": m.get("description", "") or "", "i": mid})
await sor.sqlExe("COMMIT", {})
updated.append(vmid)
continue
mid = getID()
await sor.C("llm_model", {
"id": mid, "vendor_id": vendor_id, "account_id": "",
"name": vmid, "vendor_model_id": vmid, "capability": cap,
"sync_mode": "sync", "profile_id": profile_ids.get(cap, ""),
"ppid": "", "price_input": price_in, "price_output": price_out,
"cost_input": cost_in, "cost_output": cost_out,
"default_params": "{}", "status": "active",
"description": m.get("description", "") or "", "org_id": "0"})
created.append(vmid)
rt_note = ""
if protocol not in RUNTIME_PROTOCOLS:
rt_note = ("⚠️ 协议「%s」的适配模板已存档,但当前运行时调用链仅支持 %s——"
"该供应商模型暂不可被产线直接调用" % (protocol, "/".join(RUNTIME_PROTOCOLS)))
return json.dumps({
"vendor": vendor_action, "profiles": profile_ids,
"models_created": created, "models_updated": updated, "models_skipped": skipped,
"runtime_note": rt_note,
}, ensure_ascii=False)
def _default_templates(protocol: str, capability: str, spec: dict) -> tuple:
"""生成适配模板默认值headers/data/response
OpenAI 兼容协议用平台标准模板其他协议按文档格式生成骨架供人工微调
"""
chat_path = (spec.get("chat_path") or "/chat/completions").strip()
if protocol == "openai_compat":
req = json.dumps({
"path": chat_path, "method": "POST",
"headers": {"Authorization": "Bearer {{api_key}}",
"Content-Type": "application/json"},
"params": {},
"data": {"model": "{{model}}", "messages": "{{messages}}",
"temperature": "{{temperature}}", "stream": False},
}, ensure_ascii=False)
resp = json.dumps({
"content": "choices[0].message.content",
"usage": {"prompt_tokens": "usage.prompt_tokens",
"completion_tokens": "usage.completion_tokens"},
}, ensure_ascii=False)
return req, resp
# 非标准协议:骨架模板(字段路径来自文档提取),标注需人工核对
req = json.dumps({
"path": chat_path, "method": "POST",
"headers": {"Authorization": "Bearer {{api_key}}",
"Content-Type": "application/json"},
"params": {},
"data": {"__from_doc__": spec.get("request_fields") or []},
"__note__": "非 openai_compat 协议骨架模板,需按文档人工核对",
}, ensure_ascii=False)
resp = json.dumps({
"content": spec.get("response_format", "") or "待按文档填写",
"__note__": "需人工核对",
}, ensure_ascii=False)
return req, resp
# ────────────────────── 工具 5模块/应用信息查询 ──────────────────────
async def _h_platform_modules(sor, params, ctx):
"""列出平台已装载的业务模块(内部 agent 了解平台构成用)。"""
err = await _require_admin(sor, ctx)
if err:
return err
mods = []
for name, title in [
("pipeline_core", "产线核心(会话/技能/项目)"),
("pipeline_service", "执行引擎(任务链/角色/治理)"),
("pipeline-llm", "模型治理(供应商/账号/模型/定价/记账)"),
("pipeline-bidding", "投标产线"),
("pipeline-opportunity", "商机产线"),
("pipeline-sdlc", "开发产线前端"),
("pipeline-ops", "运维"),
("pipeline-dist", "分销"),
("pipeline-task", "任务中心"),
]:
try:
__import__(name.replace("-", "_"))
loaded = True
except Exception:
loaded = False
mods.append({"module": name, "title": title, "loaded": loaded})
return json.dumps(mods, ensure_ascii=False)
# ────────────────────── 工具定义 ──────────────────────
PLATFORM_TOOLS = [
ToolDefinition(
name="platform_llm_status",
description="查看模型治理状态:供应商/账号余额/模型定价/各状态分布。用户问「模型配置状态/账号余额」时调用。仅管理员可用。",
parameters={},
category="platform",
),
ToolDefinition(
name="fetch_model_doc",
description="抓取大模型供应商的 API 文档页面(返回纯文本)。配置新模型前先用此抓取官方文档。仅管理员可用。",
parameters={"url": "文档页面 URL必须是公网 http/https"},
category="platform",
),
ToolDefinition(
name="extract_llm_api_spec",
description="通读文档文本LLM 提取 API 配置规格(端点/协议/请求响应格式/定价)。配合 fetch_model_doc 使用。仅管理员可用。",
parameters={"doc_text": "fetch_model_doc 返回的文档文本"},
category="platform",
),
ToolDefinition(
name="apply_llm_config",
description="按提取的规格写入模型治理配置:供应商/端点/适配模板/模型(含定价)。幂等——已有模型只更新定价。仅管理员可用。",
parameters={"spec": "extract_llm_api_spec 返回的 JSON 规格"},
category="platform",
requires_confirmation=True,
),
ToolDefinition(
name="platform_modules",
description="列出平台已装载的业务模块清单。用户问「平台有哪些模块」时调用。仅管理员可用。",
parameters={},
category="platform",
),
]
PLATFORM_PROMPT = """
你是产线平台的内部运维 agent服务对象是平台管理员
## 模型自动配置工作流(用户给你文档 URL 要求配置模型时)
1. `fetch_model_doc` 抓取官方文档页面
2. `extract_llm_api_spec` 通读文档提取配置规格**定价只允许来自文档原文**文档没写的价格一律 null禁止编造
3. 把提取结果摘要给用户确认尤其定价和端点用户确认后
4. `apply_llm_config` 写入幂等已有模型只更新定价
## 硬规则
- 定价单位统一/千tokens文档给百万价要 ÷1000
- cost_*供应商成本未知时与 price_* 相同
- openai_compat 协议的模型模板会存档但运行时暂不可调用必须如实告知
- 文档抓取失败/内容不足时如实说明不要凭记忆编造 API 格式
- 所有工具仅管理员可用权限报错时如实转告用户
## 平台知识
平台模块清单用 `platform_modules` 查询技能库中平台相关技能skills_library/all/
pipelines/platform_general/包含各模块的开发规范与踩坑记录需要时用 load_skill 加载
"""
PLATFORM_HANDLERS = {
"platform_llm_status": _h_platform_llm_status,
"fetch_model_doc": _h_fetch_model_doc,
"extract_llm_api_spec": _h_extract_llm_api_spec,
"apply_llm_config": _h_apply_llm_config,
"platform_modules": _h_platform_modules,
}
def register_platform_ability():
"""注册平台内部 agent 能力包(幂等)。"""
ability = PipelineAbility(
pipeline_id=PLATFORM_PIPELINE_ID,
name="平台内部 agent",
tools=PLATFORM_TOOLS,
system_prompt=PLATFORM_PROMPT,
handlers=PLATFORM_HANDLERS,
roles=[],
menus=[
{"label": "📊 模型治理", "icon": "", "url": "/pipeline-llm",
"type": "tab"},
],
)
register_ability(ability)
return ability
register_platform_ability()

9
pyproject.toml Normal file
View File

@ -0,0 +1,9 @@
[project]
name = "pipeline_platform"
version = "0.1.0"
description = "平台内部 agent平台运维/管理会话能力(模型自动配置等),仅管理员可用"
dependencies = []
[tool.setuptools.packages.find]
where = ["."]
include = ["pipeline_platform*"]

View File

@ -0,0 +1,65 @@
{
"widgettype": "VBox",
"options": {"width": "100%", "height": "100%", "padding": "0"},
"subwidgets": [
{
"widgettype": "HBox",
"options": {"width": "100%", "alignItems": "center", "padding": "16px 24px 8px 24px", "cheight": 6, "gap": "12px"},
"subwidgets": [
{"widgettype": "Title2", "options": {"text": "平台内部助手"}},
{"widgettype": "Text", "options": {"text": "平台运维/管理 agent · 模型自动配置 · 仅管理员可用", "cfontsize": 0.9, "color": "#94a3b8"}},
{"widgettype": "Filler"},
{
"widgettype": "Button",
"id": "new_session_btn",
"options": {"label": " 新建会话", "css": "small"},
"binds": [{
"wid": "self",
"event": "click",
"actiontype": "script",
"target": "self",
"script": "var tp=bricks.getWidgetById('platform_tabs',bricks.app);if(!tp)return;var sid='p'+Date.now()+'_'+Math.floor(Math.random()*100000);var n=tp.opts.items.length+1;tp.open_tab({name:'psession_'+sid,label:'会话 '+n,removable:true,content:{widgettype:'VBox',options:{css:'filler',width:'100%',height:'100%'},subwidgets:[{widgettype:'urlwidget',options:{url:'/pipeline_core/api/agent_menus.dspy?session_id='+sid+'&pipeline_id=platform_general',method:'GET'}},{widgettype:'AgentIO',options:{css:'filler',margin:'0 24px 24px 24px',url:'/pipeline_core/api/agent_chat.dspy?session_id='+sid+'&pipeline_id=platform_general',model_dataurl:'/pipeline_core/api/agent_model_options.dspy?session_id='+sid+'&pipeline_id=platform_general',model_cwidth:14,placeholder:'例如:通读这份文档并配置模型 <文档URL>...'}}]}});"
}]
}
]
},
{
"widgettype": "TabPanel",
"id": "platform_tabs",
"options": {
"css": "filler",
"width": "100%",
"height": "100%",
"tab_pos": "top",
"items": [
{
"name": "platform_default",
"label": "会话 1",
"removable": false,
"content": {
"widgettype": "VBox",
"options": {"css": "filler", "width": "100%", "height": "100%"},
"subwidgets": [
{
"widgettype": "urlwidget",
"options": {"url": "{{entire_url('/pipeline_core/api/agent_menus.dspy')}}?session_id=default_platform_general&pipeline_id=platform_general", "method": "GET"}
},
{
"widgettype": "AgentIO",
"options": {
"css": "filler",
"margin": "0 24px 24px 24px",
"url": "/pipeline_core/api/agent_chat.dspy?session_id=default_platform_general&pipeline_id=platform_general",
"model_dataurl": "/pipeline_core/api/agent_model_options.dspy?session_id=default_platform_general&pipeline_id=platform_general",
"model_cwidth": 14,
"placeholder": "例如:通读这份文档并配置模型 <文档URL>..."
}
}
]
}
}
]
}
}
]
}