mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 06:11:58 +00:00
数据目录 F2b / B3 跟进:合入 F2a 之后一对多关联后重复计算也可能只是提醒级(只凭外键推出的一对多),发布前检查不能再按规则编号认级别——服务端最小改动:门禁里 SQL 检查的问题带上检查本来的级别 sql_level(别的问题不带、序列化时不出现),界面按它写「错误 / 提醒」和拦不拦;老服务端不给时按受管档的 level 认;补后端测试和检查脚本
This commit is contained in:
@@ -47,9 +47,9 @@ def lint_for_publish(spec: GraphSpec, *, level: str, checkers: Mapping[str, Any]
|
||||
strict = level == "governed"
|
||||
|
||||
def flag(message: str, *, code: str, node_id: str | None = None, hard: bool = False,
|
||||
field: str | None = None) -> None:
|
||||
field: str | None = None, sql_level: str | None = None) -> None:
|
||||
result.add(message, level="error" if (hard and strict) else "warning", node_id=node_id,
|
||||
field=field, code=code)
|
||||
field=field, code=code, sql_level=sql_level)
|
||||
|
||||
has_contract_output = False
|
||||
for node in spec.nodes:
|
||||
@@ -139,7 +139,9 @@ def _lint_sql(spec: GraphSpec, flag: Callable[..., None], checkers: Mapping[str,
|
||||
for node, check in graph_checks(spec.nodes, checkers):
|
||||
if check.level == "info":
|
||||
continue
|
||||
flag(check.message, code=check.code, node_id=node.id, hard=check.level == "error", field="args.sql")
|
||||
# sql_level:检查本来的级别。已发布档 level 一律 warning,界面靠它说清这是错误级、只是这一档不拦
|
||||
flag(check.message, code=check.code, node_id=node.id, hard=check.level == "error", field="args.sql",
|
||||
sql_level=check.level)
|
||||
|
||||
|
||||
#: validate 里和门禁 G4、G5 说同一件事的两条提示(schema.evidence_issues)
|
||||
|
||||
@@ -154,6 +154,10 @@ class ValidationIssue(BaseModel):
|
||||
code: str | None = None
|
||||
#: 这条问题对应的修复 id(engine/autofix.py 算出来的)。只在发布检查的结果里填
|
||||
fix: str | None = None
|
||||
#: 发布门禁里基于数据目录的 SQL 检查(governance._lint_sql):这条检查本来的级别(error / warning)。level 说的是
|
||||
#: 拦不拦(已发布档一律 warning),前端要按它说清「错误级问题,已发布只提醒、受管会拦」。别的问题没有这一项,
|
||||
#: 序列化时也不出现
|
||||
sql_level: Literal["error", "warning"] | None = Field(default=None, exclude_if=lambda v: v is None)
|
||||
|
||||
|
||||
class ValidationResult(BaseModel):
|
||||
@@ -161,10 +165,11 @@ class ValidationResult(BaseModel):
|
||||
issues: list[ValidationIssue] = Field(default_factory=list)
|
||||
|
||||
def add(self, message: str, *, level: str = "error", node_id: str | None = None,
|
||||
edge_id: str | None = None, field: str | None = None, code: str | None = None) -> None:
|
||||
edge_id: str | None = None, field: str | None = None, code: str | None = None,
|
||||
sql_level: str | None = None) -> None:
|
||||
self.issues.append(
|
||||
ValidationIssue(level=level, message=message, node_id=node_id, # type: ignore[arg-type]
|
||||
edge_id=edge_id, field=field, code=code)
|
||||
edge_id=edge_id, field=field, code=code, sql_level=sql_level) # type: ignore[arg-type]
|
||||
)
|
||||
if level == "error":
|
||||
self.ok = False
|
||||
|
||||
@@ -84,8 +84,14 @@ async def test_governed_level_blocks_error_level_sql_problems(scenic_db):
|
||||
[issue] = [i for i in issues if i.code == "fanout_sum"]
|
||||
assert issue.message.startswith("「订单」关联「订单明细」是一对多")
|
||||
# 已发布级别:同一个问题只警告
|
||||
assert _sql_issues(publish_issues(spec, level="published", checkers=checkers)) == [
|
||||
("fanout_sum", "warning", "fetch", "args.sql")]
|
||||
published = publish_issues(spec, level="published", checkers=checkers)
|
||||
assert _sql_issues(published) == [("fanout_sum", "warning", "fetch", "args.sql")]
|
||||
# 拦不拦(level)之外,带着检查本来的级别(sql_level):界面据此说「错误级问题,已发布只提醒、受管会拦」
|
||||
assert [i.sql_level for i in issues if i.field == "args.sql"] == ["error"]
|
||||
assert [i.sql_level for i in published if i.field == "args.sql"] == ["error"]
|
||||
# 别的问题没有这一项,序列化时也不出现
|
||||
assert all("sql_level" not in i.model_dump() for i in published if i.field != "args.sql")
|
||||
assert [i.model_dump()["sql_level"] for i in published if i.field == "args.sql"] == ["error"]
|
||||
# 依据只是推断:info 不进门禁
|
||||
assert _sql_issues(lint_for_publish(spec, level="governed", checkers=await _checkers(scenic_db, "proposed"))
|
||||
.issues) == []
|
||||
@@ -153,6 +159,10 @@ async def test_publish_endpoints_block_governed_fanout(client, scenic_source):
|
||||
assert checked["ok"] is False
|
||||
[issue] = [i for i in checked["issues"] if i["code"] == "fanout_sum"]
|
||||
assert issue["level"] == "error" and issue["node_id"] == "fetch" and issue["field"] == "args.sql"
|
||||
assert issue["sql_level"] == "error"
|
||||
hint = (await client.post(f"/api/workflows/{wid}/publish-check", json={"level": "published"})).json()
|
||||
assert [(i["level"], i["sql_level"]) for i in hint["issues"] if i["code"] == "fanout_sum"] == [("warning", "error")]
|
||||
assert all("sql_level" not in i for i in hint["issues"] if i.get("field") != "args.sql")
|
||||
assert issue["fix"] is None, "SQL 写错没有确定性的修复,交给人或助手改"
|
||||
|
||||
fixed = (await client.post(f"/api/workflows/{wid}/autofix", json={"level": "governed", "apply": []})).json()
|
||||
|
||||
@@ -445,13 +445,15 @@ export function usePreflight(workflowId: string | undefined, level: PublishLevel
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布前检查里一条 SQL 检查问题原本的级别。门禁只收错误和提醒两级(提示的依据只是推断,不进门禁),问题的 level 换成了
|
||||
* 「拦不拦」:受管档错误级拦(error),其余都是 warning——原来是哪一级看不出来了。错误级只有两条规则
|
||||
* (服务端 sqlcheck._ERROR_CODES:一对多关联后重复计算、存量跨期加总),按规则编号认回来
|
||||
* 发布前检查里一条 SQL 检查问题本来的级别。问题的 level 说的是「拦不拦」(已发布档一律 warning),本来是错误还是提醒
|
||||
* 看服务端给的 sql_level(governance._lint_sql)。老服务端不给时按受管档的 level 认;已发布档就认不出来,按提醒说,
|
||||
* 不把拿不准的说重
|
||||
*/
|
||||
const SQL_ERROR_CODES = new Set(['fanout_sum', 'stock_summed'])
|
||||
const publishSqlLevel = (code: unknown): 'error' | 'warning' | null =>
|
||||
(isSqlCheckCode(code) ? (SQL_ERROR_CODES.has(code) ? 'error' : 'warning') : null)
|
||||
const publishSqlLevel = (issue: ValidationIssue): 'error' | 'warning' | null => {
|
||||
if (!isSqlCheckCode(issue.code)) return null
|
||||
if (issue.sql_level === 'error' || issue.sql_level === 'warning') return issue.sql_level
|
||||
return issue.level === 'error' ? 'error' : 'warning'
|
||||
}
|
||||
|
||||
function fixLabelOf(data: PublishCheck | undefined, id: string): string {
|
||||
return data?.fixes.find((f) => f.id === id)?.label ?? ''
|
||||
@@ -478,7 +480,7 @@ export function Preflight({ pf, gate, onLocate, stale }: {
|
||||
const errors = (rows ?? []).filter((r) => r.issue.level === 'error').length
|
||||
const warns = (rows ?? []).length - errors
|
||||
// 已发布档只提醒、不拦的 SQL 检查错误级问题:结论那句话里点出来,别和「没指定模型」这类提示混成一个数
|
||||
const sqlErrors = (rows ?? []).filter((r) => r.issue.level !== 'error' && publishSqlLevel(r.issue.code) === 'error').length
|
||||
const sqlErrors = (rows ?? []).filter((r) => r.issue.level !== 'error' && publishSqlLevel(r.issue) === 'error').length
|
||||
const canFix = !lock && check.status !== 'unsupported'
|
||||
const autoIds = issues ? autoFixIds(issues, fixes) : []
|
||||
const labelOf = (id?: string | null) => (id ? nodeOf(id)?.label || id : '')
|
||||
@@ -564,7 +566,7 @@ function PreflightRow({ issue, others, fix, where, pf, onLocate }: {
|
||||
}) {
|
||||
const err = issue.level === 'error'
|
||||
const Icon = err ? XCircle : AlertTriangle
|
||||
const sqlLevel = publishSqlLevel(issue.code)
|
||||
const sqlLevel = publishSqlLevel(issue)
|
||||
return (
|
||||
<li data-preflight-issue={issue.code ?? ''} data-fix-kind={fix?.kind} data-sql-check-level={sqlLevel ?? undefined}>
|
||||
<button
|
||||
|
||||
@@ -1346,6 +1346,11 @@ export interface ValidationIssue {
|
||||
code?: string | null
|
||||
/** 这条问题对应的修复 id:publish-check 的 fixes 里同 id 的那一项。没有修复时为空 */
|
||||
fix?: string | null
|
||||
/**
|
||||
* 发布门禁里的 SQL 检查:这条检查本来的级别。level 说的是拦不拦(已发布档一律 warning),它说的是错误还是提醒。
|
||||
* 别的问题、老服务端不给
|
||||
*/
|
||||
sql_level?: 'error' | 'warning' | null
|
||||
}
|
||||
|
||||
/** 发布等级:发布弹窗、问题面板的发布前检查共用 */
|
||||
|
||||
@@ -216,7 +216,7 @@ function lint(graph, level, { followsNever = false } = {}) {
|
||||
const c = n.data.config ?? {}
|
||||
// 基于数据目录的 SQL 检查(governance._lint_sql):错误级在受管档是硬性问题,已发布档只提示;没有确定性的修复
|
||||
if (n.type === 'tool' && /JOIN order_items/i.test(c.args?.sql ?? '')) {
|
||||
issues.push({ level: hard ? 'error' : 'warning', node_id: n.id, code: 'fanout_sum', fix: null, field: 'args.sql',
|
||||
issues.push({ level: hard ? 'error' : 'warning', sql_level: 'error', node_id: n.id, code: 'fanout_sum', fix: null, field: 'args.sql',
|
||||
message: '「订单」关联「订单明细」是一对多,对「订单」的「订单金额」求和会重复计算。请先按订单汇总明细再关联' })
|
||||
}
|
||||
if (followsNever && n.type === 'agent' && !c.approval && (c.tools ?? []).length) {
|
||||
|
||||
Reference in New Issue
Block a user