opencode-skills-collection 4.0.69 → 4.0.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled-skills/.antigravity-install-manifest.json +39 -1
- package/bundled-skills/api-integration-architect/SKILL.md +241 -0
- package/bundled-skills/apify-generate-output-schema/SKILL.md +438 -0
- package/bundled-skills/apify-integration-development/SKILL.md +168 -0
- package/bundled-skills/apify-integration-development/references/ai-framework-package.md +158 -0
- package/bundled-skills/apify-integration-development/references/ai-harness-plugin.md +192 -0
- package/bundled-skills/apify-integration-development/references/sdk-integration.md +236 -0
- package/bundled-skills/apify-integration-development/references/workflow-automation.md +163 -0
- package/bundled-skills/architecture-review/README.md +42 -0
- package/bundled-skills/architecture-review/SKILL.md +77 -0
- package/bundled-skills/architecture-review/examples.md +11 -0
- package/bundled-skills/architecture-review/reference/best-practices.md +7 -0
- package/bundled-skills/architecture-review/reference/capabilities.md +20 -0
- package/bundled-skills/architecture-review/reference/fallbacks.md +11 -0
- package/bundled-skills/architecture-review/reference/graph.md +15 -0
- package/bundled-skills/architecture-review/reference/mcp.md +14 -0
- package/bundled-skills/architecture-review/reference/workflow.md +15 -0
- package/bundled-skills/architecture-review/templates/architecture-review.md +21 -0
- package/bundled-skills/code-review-sensei/SKILL.md +177 -0
- package/bundled-skills/codebase-onboarding/README.md +42 -0
- package/bundled-skills/codebase-onboarding/SKILL.md +77 -0
- package/bundled-skills/codebase-onboarding/examples.md +11 -0
- package/bundled-skills/codebase-onboarding/reference/best-practices.md +7 -0
- package/bundled-skills/codebase-onboarding/reference/capabilities.md +20 -0
- package/bundled-skills/codebase-onboarding/reference/fallbacks.md +11 -0
- package/bundled-skills/codebase-onboarding/reference/graph.md +15 -0
- package/bundled-skills/codebase-onboarding/reference/mcp.md +14 -0
- package/bundled-skills/codebase-onboarding/reference/workflow.md +15 -0
- package/bundled-skills/codebase-onboarding/templates/repository-onboarding.md +21 -0
- package/bundled-skills/connection-auth-rules/SKILL.md +199 -0
- package/bundled-skills/connection-auth-rules/fetch_schema.py +320 -0
- package/bundled-skills/dependency-analysis/README.md +42 -0
- package/bundled-skills/dependency-analysis/SKILL.md +76 -0
- package/bundled-skills/dependency-analysis/examples.md +11 -0
- package/bundled-skills/dependency-analysis/reference/best-practices.md +7 -0
- package/bundled-skills/dependency-analysis/reference/capabilities.md +20 -0
- package/bundled-skills/dependency-analysis/reference/fallbacks.md +11 -0
- package/bundled-skills/dependency-analysis/reference/graph.md +15 -0
- package/bundled-skills/dependency-analysis/reference/mcp.md +14 -0
- package/bundled-skills/dependency-analysis/reference/workflow.md +15 -0
- package/bundled-skills/dependency-analysis/templates/dependency-review.md +21 -0
- package/bundled-skills/devops-pipeline-builder/SKILL.md +200 -0
- package/bundled-skills/eas-app-stores/SKILL.md +197 -0
- package/bundled-skills/eas-app-stores/agents/openai.yaml +4 -0
- package/bundled-skills/eas-app-stores/references/app-store-metadata.md +497 -0
- package/bundled-skills/eas-app-stores/references/ios-app-store.md +376 -0
- package/bundled-skills/eas-app-stores/references/native-ios.md +167 -0
- package/bundled-skills/eas-app-stores/references/play-store.md +244 -0
- package/bundled-skills/eas-app-stores/references/testflight.md +62 -0
- package/bundled-skills/eas-app-stores/references/workflows.md +120 -0
- package/bundled-skills/eas-hosting/SKILL.md +448 -0
- package/bundled-skills/eas-hosting/agents/openai.yaml +4 -0
- package/bundled-skills/eas-observe/SKILL.md +75 -0
- package/bundled-skills/eas-observe/agents/openai.yaml +4 -0
- package/bundled-skills/eas-observe/references/metrics.md +98 -0
- package/bundled-skills/eas-observe/references/queries.md +403 -0
- package/bundled-skills/eas-observe/references/setup.md +476 -0
- package/bundled-skills/eas-observe/references/third-party.md +136 -0
- package/bundled-skills/eas-simulator/SKILL.md +251 -0
- package/bundled-skills/eas-simulator/agents/openai.yaml +4 -0
- package/bundled-skills/eas-simulator/references/controllers.md +135 -0
- package/bundled-skills/eas-simulator/references/run-your-app.md +240 -0
- package/bundled-skills/eas-simulator/references/troubleshooting.md +47 -0
- package/bundled-skills/eas-workflows/SKILL.md +119 -0
- package/bundled-skills/eas-workflows/agents/openai.yaml +4 -0
- package/bundled-skills/eas-workflows/scripts/fetch.js +109 -0
- package/bundled-skills/expo-animation/LICENSE +21 -0
- package/bundled-skills/expo-animation/RECIPES.md +385 -0
- package/bundled-skills/expo-animation/SKILL.md +295 -0
- package/bundled-skills/expo-animation/agents/openai.yaml +4 -0
- package/bundled-skills/fact-check-x-unified/SKILL.md +178 -0
- package/bundled-skills/fact-check-x-unified/agents/openai.yaml +4 -0
- package/bundled-skills/fact-check-x-unified/references/acceptance-criteria.md +44 -0
- package/bundled-skills/fact-check-x-unified/references/contracts.md +39 -0
- package/bundled-skills/fact-check-x-unified/scripts/common.py +31 -0
- package/bundled-skills/fact-check-x-unified/scripts/fact_check_x.py +1832 -0
- package/bundled-skills/fact-check-x-unified/scripts/trusted_search_config.py +324 -0
- package/bundled-skills/fact-check-x-unified/tests/anchor_downgrade_test.py +90 -0
- package/bundled-skills/fact-check-x-unified/tests/multi_platform_test.py +369 -0
- package/bundled-skills/fact-check-x-unified/tests/smoke_test.py +740 -0
- package/bundled-skills/fact-check-x-unified/tests/stage_checkpoint_test.py +103 -0
- package/bundled-skills/fact-check-x-unified/tests/trusted_search_config_test.py +156 -0
- package/bundled-skills/gpt-taste/SKILL.md +8 -1
- package/bundled-skills/hf-cli/SKILL.md +263 -0
- package/bundled-skills/huggingface-community-evals/SKILL.md +228 -0
- package/bundled-skills/huggingface-community-evals/examples/.env.example +3 -0
- package/bundled-skills/huggingface-community-evals/examples/USAGE_EXAMPLES.md +101 -0
- package/bundled-skills/huggingface-community-evals/scripts/inspect_eval_uv.py +104 -0
- package/bundled-skills/huggingface-community-evals/scripts/inspect_vllm_uv.py +306 -0
- package/bundled-skills/huggingface-community-evals/scripts/lighteval_vllm_uv.py +297 -0
- package/bundled-skills/huggingface-datasets/SKILL.md +130 -0
- package/bundled-skills/jev-social/SKILL.md +182 -0
- package/bundled-skills/longbridge-derivatives/SKILL.md +117 -0
- package/bundled-skills/longbridge-derivatives/references/option.md +36 -0
- package/bundled-skills/longbridge-derivatives/references/options-advanced.md +101 -0
- package/bundled-skills/longbridge-derivatives/references/options-pnl.md +74 -0
- package/bundled-skills/longbridge-derivatives/references/options-strategy.md +82 -0
- package/bundled-skills/longbridge-derivatives/references/options-volatility.md +70 -0
- package/bundled-skills/longbridge-derivatives/references/warrant.md +12 -0
- package/bundled-skills/longbridge-quant/SKILL.md +151 -0
- package/bundled-skills/longbridge-quant/references/correlation.md +51 -0
- package/bundled-skills/longbridge-quant/references/execution-model.md +68 -0
- package/bundled-skills/longbridge-quant/references/factor-research.md +95 -0
- package/bundled-skills/longbridge-quant/references/factor-screen.md +101 -0
- package/bundled-skills/longbridge-quant/references/hedging.md +136 -0
- package/bundled-skills/longbridge-quant/references/ml-strategy.md +77 -0
- package/bundled-skills/longbridge-quant/references/multifactor.md +68 -0
- package/bundled-skills/longbridge-quant/references/pairs-trading.md +61 -0
- package/bundled-skills/longbridge-quant/references/quant-cli.md +133 -0
- package/bundled-skills/longbridge-quant/references/quant-stats.md +150 -0
- package/bundled-skills/longbridge-quant/references/seasonality.md +50 -0
- package/bundled-skills/longbridge-quant/references/strategy-optimizer.md +68 -0
- package/bundled-skills/longbridge-quant/references/volatility-strategy.md +52 -0
- package/bundled-skills/longbridge-research/SKILL.md +187 -0
- package/bundled-skills/longbridge-research/references/company-profile.md +96 -0
- package/bundled-skills/longbridge-research/references/company-tearsheet.md +82 -0
- package/bundled-skills/longbridge-research/references/competitive-analysis.md +81 -0
- package/bundled-skills/longbridge-research/references/consensus.md +92 -0
- package/bundled-skills/longbridge-research/references/coverage-initiation.md +76 -0
- package/bundled-skills/longbridge-research/references/defi-yield.md +60 -0
- package/bundled-skills/longbridge-research/references/finance-calendar.md +165 -0
- package/bundled-skills/longbridge-research/references/financial-planning.md +77 -0
- package/bundled-skills/longbridge-research/references/forecast-eps.md +39 -0
- package/bundled-skills/longbridge-research/references/fund-holder.md +44 -0
- package/bundled-skills/longbridge-research/references/hkipo-analysis.md +101 -0
- package/bundled-skills/longbridge-research/references/industry-peers.md +46 -0
- package/bundled-skills/longbridge-research/references/industry-rank.md +62 -0
- package/bundled-skills/longbridge-research/references/insider-trades.md +48 -0
- package/bundled-skills/longbridge-research/references/institution-rating.md +62 -0
- package/bundled-skills/longbridge-research/references/investment-ideas.md +69 -0
- package/bundled-skills/longbridge-research/references/investment-proposal.md +95 -0
- package/bundled-skills/longbridge-research/references/investors.md +87 -0
- package/bundled-skills/longbridge-research/references/onchain.md +70 -0
- package/bundled-skills/longbridge-research/references/post-investment.md +76 -0
- package/bundled-skills/longbridge-research/references/shareholder.md +72 -0
- package/bundled-skills/longbridge-research/references/short-positions.md +50 -0
- package/bundled-skills/longbridge-research/references/short-trades.md +50 -0
- package/bundled-skills/longbridge-research/references/stock-research.md +61 -0
- package/bundled-skills/longbridge-research/references/thesis-tracker.md +64 -0
- package/bundled-skills/makepad-2-0-animation/SKILL.md +318 -0
- package/bundled-skills/makepad-2-0-animation/references/animator-reference.md +433 -0
- package/bundled-skills/makepad-2-0-dsl/SKILL.md +492 -0
- package/bundled-skills/makepad-2-0-dsl/references/dsl-syntax-reference.md +511 -0
- package/bundled-skills/makepad-2-0-dsl/references/extended-guide.md +56 -0
- package/bundled-skills/makepad-2-0-dsl/references/property-system.md +757 -0
- package/bundled-skills/makepad-2-0-events/SKILL.md +497 -0
- package/bundled-skills/makepad-2-0-events/references/event-patterns.md +802 -0
- package/bundled-skills/makepad-2-0-events/references/extended-guide.md +590 -0
- package/bundled-skills/makepad-2-0-layout/SKILL.md +499 -0
- package/bundled-skills/makepad-2-0-layout/references/extended-guide.md +243 -0
- package/bundled-skills/makepad-2-0-layout/references/layout-patterns.md +881 -0
- package/bundled-skills/makepad-2-0-widgets/SKILL.md +261 -0
- package/bundled-skills/makepad-2-0-widgets/references/widget-advanced.md +648 -0
- package/bundled-skills/makepad-2-0-widgets/references/widget-catalog.md +547 -0
- package/bundled-skills/meeting-distiller-pro/SKILL.md +120 -0
- package/bundled-skills/monte-carlo-analyze-root-cause/SKILL.md +12 -1
- package/bundled-skills/monte-carlo-asset-health/SKILL.md +12 -1
- package/bundled-skills/monte-carlo-context-detection/SKILL.md +170 -0
- package/bundled-skills/monte-carlo-context-detection/references/signal-definitions.md +46 -0
- package/bundled-skills/remotion-captions/SKILL.md +57 -0
- package/bundled-skills/remotion-captions/agents/openai.yaml +7 -0
- package/bundled-skills/remotion-captions/assets/remotion-icon.svg +4 -0
- package/bundled-skills/remotion-captions/display-captions.md +190 -0
- package/bundled-skills/remotion-captions/import-srt-captions.md +73 -0
- package/bundled-skills/remotion-captions/transcribe-captions.md +70 -0
- package/bundled-skills/remotion-create/SKILL.md +106 -0
- package/bundled-skills/remotion-create/agents/openai.yaml +7 -0
- package/bundled-skills/remotion-create/assets/remotion-icon.svg +4 -0
- package/bundled-skills/remotion-create/tailwind.md +11 -0
- package/bundled-skills/remotion-create/video-layout.md +9 -0
- package/bundled-skills/remotion-docs/SKILL.md +67 -0
- package/bundled-skills/remotion-docs/agents/openai.yaml +7 -0
- package/bundled-skills/remotion-docs/assets/remotion-icon.svg +4 -0
- package/bundled-skills/remotion-interactivity/SKILL.md +270 -0
- package/bundled-skills/remotion-interactivity/agents/openai.yaml +7 -0
- package/bundled-skills/remotion-interactivity/assets/remotion-icon.svg +4 -0
- package/bundled-skills/remotion-render/SKILL.md +48 -0
- package/bundled-skills/remotion-render/agents/openai.yaml +7 -0
- package/bundled-skills/remotion-render/assets/remotion-icon.svg +4 -0
- package/bundled-skills/remotion-render/transparent-videos.md +106 -0
- package/bundled-skills/saas-pricing-strategist/SKILL.md +169 -0
- package/bundled-skills/score-eval/SKILL.md +35 -0
- package/bundled-skills/writing-guidelines/SKILL.md +60 -0
- package/package.json +1 -1
- package/skills_index.json +980 -3
|
@@ -0,0 +1,740 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
import tempfile
|
|
10
|
+
import zipfile
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from unittest.mock import patch
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
16
|
+
SKILLS = ROOT.parent
|
|
17
|
+
if (ROOT / "modules").exists():
|
|
18
|
+
SKILLS = ROOT / "modules"
|
|
19
|
+
COMPARE_FIXTURES = SKILLS / "fact-check-x-knowledge-compare" / "tests" / "fixtures"
|
|
20
|
+
AUTHORITY_FIXTURES = SKILLS / "fact-check-x-authoritative-verify" / "tests" / "fixtures"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def run(arguments: list[str], environment: dict[str, str] | None = None) -> dict:
|
|
24
|
+
process = subprocess.run(arguments, text=True, capture_output=True, check=False, env=environment)
|
|
25
|
+
if process.returncode:
|
|
26
|
+
raise AssertionError(process.stdout or process.stderr)
|
|
27
|
+
payload = json.loads([line for line in process.stdout.splitlines() if line.strip()][-1])
|
|
28
|
+
checkpoint = payload.get("checkpoint") or {}
|
|
29
|
+
acknowledgement = checkpoint.get("acknowledgement") or {}
|
|
30
|
+
if checkpoint.get("status") == "awaiting_user" and acknowledgement:
|
|
31
|
+
acknowledged = subprocess.run(
|
|
32
|
+
[sys.executable, str(ROOT / "scripts" / "fact_check_x.py"),
|
|
33
|
+
"acknowledge-stage", "--run-dir", str(Path(checkpoint["path"]).parent),
|
|
34
|
+
"--stage", checkpoint["stage"], "--token", acknowledgement["token"],
|
|
35
|
+
"--decision", "continue"],
|
|
36
|
+
text=True, capture_output=True, check=False, env=environment,
|
|
37
|
+
)
|
|
38
|
+
if acknowledged.returncode:
|
|
39
|
+
raise AssertionError(acknowledged.stdout or acknowledged.stderr)
|
|
40
|
+
return payload
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def run_failed(arguments: list[str], environment: dict[str, str] | None = None) -> dict:
|
|
44
|
+
process = subprocess.run(arguments, text=True, capture_output=True, check=False, env=environment)
|
|
45
|
+
if process.returncode == 0:
|
|
46
|
+
raise AssertionError("命令应以非零状态结束")
|
|
47
|
+
return json.loads([line for line in process.stdout.splitlines() if line.strip()][-1])
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def command(*arguments: str) -> list[str]:
|
|
51
|
+
return [sys.executable, str(ROOT / "scripts" / "fact_check_x.py"), *arguments]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def main() -> int:
|
|
55
|
+
with tempfile.TemporaryDirectory(prefix="fact-check-x-unified-") as temp:
|
|
56
|
+
sys.path.insert(0, str(ROOT / "scripts"))
|
|
57
|
+
import fact_check_x
|
|
58
|
+
from fact_check_x import (
|
|
59
|
+
collect_technical_notices,
|
|
60
|
+
merge_verification,
|
|
61
|
+
normalize_report_navigation,
|
|
62
|
+
)
|
|
63
|
+
assert fact_check_x.file_uri_for_path(
|
|
64
|
+
r"C:\WorkBuddy\fact-check-x\runs\sample run\02-comparison-report.html"
|
|
65
|
+
) == "file:///C:/WorkBuddy/fact-check-x/runs/sample%20run/02-comparison-report.html"
|
|
66
|
+
assert fact_check_x.normalize_platform_path_text(
|
|
67
|
+
"/c/WorkBuddy/fact-check-x/runs/sample run/02-comparison-report.html",
|
|
68
|
+
"nt",
|
|
69
|
+
) == "C:/WorkBuddy/fact-check-x/runs/sample run/02-comparison-report.html"
|
|
70
|
+
assert fact_check_x.file_uri_for_path(
|
|
71
|
+
"/c/WorkBuddy/fact-check-x/runs/sample run/02-comparison-report.html",
|
|
72
|
+
"nt",
|
|
73
|
+
) == "file:///C:/WorkBuddy/fact-check-x/runs/sample%20run/02-comparison-report.html"
|
|
74
|
+
|
|
75
|
+
def assert_deliverable(item: dict, label: str, path: Path) -> None:
|
|
76
|
+
uri = path.resolve().as_uri()
|
|
77
|
+
assert item["label"] == label
|
|
78
|
+
assert item["path"] == str(path.resolve())
|
|
79
|
+
assert item["fileUri"] == uri
|
|
80
|
+
assert item["markdownLink"] == f"[打开{label}](<{uri}>)"
|
|
81
|
+
|
|
82
|
+
notice_run = Path(temp) / "technical-notice"
|
|
83
|
+
(notice_run / "capture").mkdir(parents=True)
|
|
84
|
+
(notice_run / "capture" / "results.json").write_text(
|
|
85
|
+
json.dumps({
|
|
86
|
+
"platforms": [{
|
|
87
|
+
"platform": "doubao",
|
|
88
|
+
"label": "豆包",
|
|
89
|
+
"references": [
|
|
90
|
+
{"sourceAcquisitionStatus": "blocked"},
|
|
91
|
+
{"contentAcquisition": "failed"},
|
|
92
|
+
],
|
|
93
|
+
}],
|
|
94
|
+
}, ensure_ascii=False),
|
|
95
|
+
encoding="utf-8",
|
|
96
|
+
)
|
|
97
|
+
notices = collect_technical_notices(notice_run)
|
|
98
|
+
assert len(notices) == 1 and "豆包 2 条" in notices[0]
|
|
99
|
+
notice_html = normalize_report_navigation(
|
|
100
|
+
b"<html><head><style></style></head><body><main>test</main></body></html>",
|
|
101
|
+
"01-capture-report.html",
|
|
102
|
+
notices,
|
|
103
|
+
).decode("utf-8")
|
|
104
|
+
assert 'data-fcx-run-notice="1"' in notice_html
|
|
105
|
+
assert "受影响且无法核验的主张按“疑似误导”呈现" in notice_html
|
|
106
|
+
|
|
107
|
+
merge_results = Path(temp) / "merge-results"
|
|
108
|
+
merge_results.mkdir()
|
|
109
|
+
(merge_results / "K1.json").write_text(json.dumps({
|
|
110
|
+
"schemaVersion": "fact-check-x/authority-result@1",
|
|
111
|
+
"requestId": "K1",
|
|
112
|
+
"searchMode": "trusted_search",
|
|
113
|
+
"requestCount": 1,
|
|
114
|
+
"authoritativeFinding": "直接问题权威结论。",
|
|
115
|
+
"claims": {
|
|
116
|
+
"doubao": {"covered": True, "claim": "直接问题平台主张。"},
|
|
117
|
+
},
|
|
118
|
+
"verdicts": {
|
|
119
|
+
"doubao": {"verdict": "contradicted"},
|
|
120
|
+
},
|
|
121
|
+
"evidenceGaps": [],
|
|
122
|
+
}), encoding="utf-8")
|
|
123
|
+
(merge_results / "K2.json").write_text(json.dumps({
|
|
124
|
+
"schemaVersion": "fact-check-x/authority-result@1",
|
|
125
|
+
"requestId": "K2",
|
|
126
|
+
"searchMode": "trusted_search",
|
|
127
|
+
"requestCount": 1,
|
|
128
|
+
"authoritativeFinding": "补充参考权威结论。",
|
|
129
|
+
"claims": {
|
|
130
|
+
"doubao": {"covered": True, "claim": "补充参考平台主张。"},
|
|
131
|
+
},
|
|
132
|
+
"verdicts": {
|
|
133
|
+
"doubao": {"verdict": "supported"},
|
|
134
|
+
},
|
|
135
|
+
"evidenceGaps": [],
|
|
136
|
+
}), encoding="utf-8")
|
|
137
|
+
merged = merge_verification({
|
|
138
|
+
"question": "测试",
|
|
139
|
+
"platforms": [{"platform": "doubao", "label": "豆包"}],
|
|
140
|
+
"knowledgePoints": [{
|
|
141
|
+
"id": "K1",
|
|
142
|
+
"description": "直接问题",
|
|
143
|
+
"role": "direct",
|
|
144
|
+
"trustedAnchor": {"eligible": False},
|
|
145
|
+
}, {
|
|
146
|
+
"id": "K2",
|
|
147
|
+
"description": "补充参考",
|
|
148
|
+
"role": "reference",
|
|
149
|
+
"trustedAnchor": {"eligible": False},
|
|
150
|
+
}],
|
|
151
|
+
"analysisGaps": [{
|
|
152
|
+
"stage": "comparison",
|
|
153
|
+
"knowledgePointId": "K1",
|
|
154
|
+
"platform": "doubao",
|
|
155
|
+
"reason": "原回答引用不足",
|
|
156
|
+
}],
|
|
157
|
+
}, merge_results)
|
|
158
|
+
assert merged["status"] == "completed"
|
|
159
|
+
assert merged["evidenceGaps"] == []
|
|
160
|
+
assert merged["finalAnswer"]["knowledgePointIds"] == ["K1"]
|
|
161
|
+
assert merged["supplementalFindings"]["knowledgePointIds"] == ["K2"]
|
|
162
|
+
assert merged["finalAnswer"]["answer"] == "直接问题权威结论。"
|
|
163
|
+
assert "补充参考权威结论" not in merged["finalAnswer"]["answer"]
|
|
164
|
+
|
|
165
|
+
run_dir = Path(temp) / "run"
|
|
166
|
+
results = COMPARE_FIXTURES / "results.json"
|
|
167
|
+
keyless_environment = {
|
|
168
|
+
**os.environ,
|
|
169
|
+
"TRUSTED_SEARCH_KEY": "",
|
|
170
|
+
"FACT_CHECK_X_TRUSTED_SEARCH_KEY_FILE": str(
|
|
171
|
+
Path(temp) / "missing-trusted-search-key"
|
|
172
|
+
),
|
|
173
|
+
}
|
|
174
|
+
located = run(command("locate"))
|
|
175
|
+
assert set(located["skills"]) == {"collector", "comparison", "authority"}
|
|
176
|
+
capture_stage = run(command("prepare-comparison", "--results", str(results), "--run-dir", str(run_dir)))
|
|
177
|
+
assert capture_stage["stage"] == "capture_completed"
|
|
178
|
+
assert capture_stage["analysisExecution"]["mode"] == "single_pass"
|
|
179
|
+
assert capture_stage["analysisExecution"]["maxToolCallsAfterStageAcknowledgement"] == 3
|
|
180
|
+
assert len(capture_stage["analysisExecution"]["instructions"]) == 3
|
|
181
|
+
assert capture_stage["artifacts"]["answerReferenceReport"] == str((run_dir / "capture" / "report.html").resolve())
|
|
182
|
+
assert capture_stage["deliverables"][0]["path"] == str((run_dir / "01-capture-report.html").resolve())
|
|
183
|
+
assert_deliverable(
|
|
184
|
+
capture_stage["deliverables"][0],
|
|
185
|
+
"各方答案汇总",
|
|
186
|
+
run_dir / "01-capture-report.html",
|
|
187
|
+
)
|
|
188
|
+
assert capture_stage["checkpoint"]["mustPresentBeforeNextStage"] is True
|
|
189
|
+
assert capture_stage["checkpoint"]["path"] == str(
|
|
190
|
+
(run_dir / "01-capture-report.html").resolve()
|
|
191
|
+
)
|
|
192
|
+
assert "打开各方答案汇总" in capture_stage["checkpoint"]["message"]
|
|
193
|
+
assert (run_dir / "01-capture-report.html").exists()
|
|
194
|
+
assert len(capture_stage["platforms"]) == 2
|
|
195
|
+
comparison_stage = run(command("complete-comparison", "--results", str(results), "--analysis", str(COMPARE_FIXTURES / "comparison-analysis.json"), "--run-dir", str(run_dir)))
|
|
196
|
+
assert comparison_stage["stage"] == "comparison_completed"
|
|
197
|
+
assert comparison_stage["knowledgePointCount"] == 1
|
|
198
|
+
assert comparison_stage["artifacts"]["comparisonAnalysis"] == str(
|
|
199
|
+
(run_dir / "comparison-analysis.json").resolve()
|
|
200
|
+
)
|
|
201
|
+
assert (run_dir / "comparison-analysis.json").is_file()
|
|
202
|
+
assert comparison_stage["artifacts"]["comparisonReport"] == str((run_dir / "comparison.html").resolve())
|
|
203
|
+
assert comparison_stage["deliverables"][0]["path"] == str((run_dir / "02-comparison-report.html").resolve())
|
|
204
|
+
assert_deliverable(
|
|
205
|
+
comparison_stage["deliverables"][0],
|
|
206
|
+
"各方答案聚合(未核验)",
|
|
207
|
+
run_dir / "02-comparison-report.html",
|
|
208
|
+
)
|
|
209
|
+
assert comparison_stage["checkpoint"]["mustPresentBeforeNextStage"] is True
|
|
210
|
+
assert comparison_stage["checkpoint"]["path"] == str(
|
|
211
|
+
(run_dir / "02-comparison-report.html").resolve()
|
|
212
|
+
)
|
|
213
|
+
overwide_analysis = json.loads(
|
|
214
|
+
(COMPARE_FIXTURES / "comparison-analysis.json").read_text(encoding="utf-8")
|
|
215
|
+
)
|
|
216
|
+
overwide_analysis["knowledgePoints"][0]["claims"]["dknowc-chat"]["claim"] = (
|
|
217
|
+
"每人每月最高提取1400元,并另有最高300万元研发资助"
|
|
218
|
+
)
|
|
219
|
+
overwide_path = Path(temp) / "overwide-comparison-analysis.json"
|
|
220
|
+
overwide_path.write_text(
|
|
221
|
+
json.dumps(overwide_analysis, ensure_ascii=False), encoding="utf-8"
|
|
222
|
+
)
|
|
223
|
+
overwide_run = Path(temp) / "overwide-run"
|
|
224
|
+
run(command(
|
|
225
|
+
"prepare-comparison",
|
|
226
|
+
"--results", str(results),
|
|
227
|
+
"--run-dir", str(overwide_run),
|
|
228
|
+
))
|
|
229
|
+
overwide_failure = run_failed(command(
|
|
230
|
+
"complete-comparison",
|
|
231
|
+
"--results", str(results),
|
|
232
|
+
"--analysis", str(overwide_path),
|
|
233
|
+
"--run-dir", str(overwide_run),
|
|
234
|
+
))
|
|
235
|
+
assert "原子性门禁" in json.dumps(overwide_failure, ensure_ascii=False)
|
|
236
|
+
assert not (overwide_run / "comparison-gate.json").exists()
|
|
237
|
+
assert "打开各方答案聚合(未核验)" in comparison_stage["checkpoint"]["message"]
|
|
238
|
+
assert (run_dir / "02-comparison-report.html").exists()
|
|
239
|
+
prepared = run(command("prepare-authority", "--run-dir", str(run_dir)), keyless_environment)
|
|
240
|
+
assert prepared["taskCount"] == 1
|
|
241
|
+
assert prepared["status"] == "prepared" and prepared["trustedSearchRequiredCount"] == 0
|
|
242
|
+
request = json.loads((run_dir / "authority" / "requests" / "K1.json").read_text(encoding="utf-8"))
|
|
243
|
+
assert set(request["cloudPayload"]) == {"title", "knowledgePoint", "differingClaims"}
|
|
244
|
+
assert "answerMarkdown" not in json.dumps(request["cloudPayload"], ensure_ascii=False)
|
|
245
|
+
searched = run(command("search-authority", "--run-dir", str(run_dir), "--max-workers", "12"), keyless_environment)
|
|
246
|
+
assert searched["trustedSearchRequestCount"] == 0 and searched["dknowExemptCount"] == 1
|
|
247
|
+
assessments = run_dir / "authority" / "assessments"
|
|
248
|
+
assessments.mkdir(parents=True)
|
|
249
|
+
assessment = json.loads((AUTHORITY_FIXTURES / "K1-assessment.json").read_text(encoding="utf-8"))
|
|
250
|
+
(assessments / "K1.json").write_text(json.dumps(assessment, ensure_ascii=False), encoding="utf-8")
|
|
251
|
+
original_pipeline_run = fact_check_x.run
|
|
252
|
+
|
|
253
|
+
def fail_authority_report(arguments, environment=None):
|
|
254
|
+
if any(Path(str(item)).name == "render_authority_report.py" for item in arguments):
|
|
255
|
+
raise fact_check_x.PipelineError("强制报告渲染失败")
|
|
256
|
+
return original_pipeline_run(arguments, environment)
|
|
257
|
+
|
|
258
|
+
with patch.object(fact_check_x, "run", side_effect=fail_authority_report):
|
|
259
|
+
try:
|
|
260
|
+
fact_check_x.finalize_authority(
|
|
261
|
+
type("Args", (), {"run_dir": str(run_dir), "assessments_dir": None})(),
|
|
262
|
+
{key: Path(value) for key, value in located["skills"].items()},
|
|
263
|
+
)
|
|
264
|
+
except fact_check_x.PipelineError as exc:
|
|
265
|
+
assert "强制报告渲染失败" in str(exc)
|
|
266
|
+
else:
|
|
267
|
+
raise AssertionError("报告渲染失败应回滚权威核验事务")
|
|
268
|
+
assert json.loads(
|
|
269
|
+
(run_dir / "authority-gate.json").read_text(encoding="utf-8")
|
|
270
|
+
)["status"] == "searched"
|
|
271
|
+
assert not list((run_dir / "authority" / "results").glob("*.json"))
|
|
272
|
+
assert not (run_dir / "verification.json").exists()
|
|
273
|
+
assert not (run_dir / "03-authority-report.html").exists()
|
|
274
|
+
finalized = run(command("finalize-authority", "--run-dir", str(run_dir)))
|
|
275
|
+
assert finalized["status"] == "completed"
|
|
276
|
+
assert finalized["stage"] == "authority_completed"
|
|
277
|
+
assert finalized["deliverables"][0]["path"] == str(
|
|
278
|
+
(run_dir / "03-authority-report.html").resolve()
|
|
279
|
+
)
|
|
280
|
+
assert_deliverable(
|
|
281
|
+
finalized["deliverables"][0],
|
|
282
|
+
"权威核验后的最终答案",
|
|
283
|
+
run_dir / "03-authority-report.html",
|
|
284
|
+
)
|
|
285
|
+
assert finalized["checkpoint"]["mustPresentBeforeNextStage"] is True
|
|
286
|
+
assert finalized["checkpoint"]["path"] == str(
|
|
287
|
+
(run_dir / "03-authority-report.html").resolve()
|
|
288
|
+
)
|
|
289
|
+
assert "打开权威核验后的最终答案" in finalized["checkpoint"]["message"]
|
|
290
|
+
assert (run_dir / "03-authority-report.html").exists()
|
|
291
|
+
assert json.loads((run_dir / "authority-gate.json").read_text(encoding="utf-8"))["status"] == "finalized"
|
|
292
|
+
reopened = run(command(
|
|
293
|
+
"reopen-authority",
|
|
294
|
+
"--run-dir", str(run_dir),
|
|
295
|
+
"--reason", "修正被截断的权威结论",
|
|
296
|
+
))
|
|
297
|
+
assert reopened["stage"] == "authority_reopened"
|
|
298
|
+
assert reopened["revision"] == 1
|
|
299
|
+
revision_dir = run_dir / "authority" / "revisions" / "revision-001"
|
|
300
|
+
assert (revision_dir / "revision.json").is_file()
|
|
301
|
+
assert (revision_dir / "verification.json").is_file()
|
|
302
|
+
assert (revision_dir / "authority" / "results" / "K1.json").is_file()
|
|
303
|
+
assert not list((run_dir / "authority" / "results").glob("*.json"))
|
|
304
|
+
assert not (run_dir / "verification.json").exists()
|
|
305
|
+
assert not (run_dir / "03-authority-report.html").exists()
|
|
306
|
+
reopened_gate = json.loads((run_dir / "authority-gate.json").read_text(encoding="utf-8"))
|
|
307
|
+
assert reopened_gate["status"] == "searched"
|
|
308
|
+
comparison_data = json.loads((run_dir / "comparison.json").read_text(encoding="utf-8"))
|
|
309
|
+
exact_title = comparison_data["knowledgePoints"][0]["description"]
|
|
310
|
+
assessment["authoritativeFinding"] = f"{exact_title}:每人每月最高提取1400元。"
|
|
311
|
+
(assessments / "K1.json").write_text(
|
|
312
|
+
json.dumps(assessment, ensure_ascii=False), encoding="utf-8"
|
|
313
|
+
)
|
|
314
|
+
refinalized = run(command("finalize-authority", "--run-dir", str(run_dir)))
|
|
315
|
+
assert refinalized["status"] == "completed"
|
|
316
|
+
reopened_verification = json.loads(
|
|
317
|
+
(run_dir / "verification.json").read_text(encoding="utf-8")
|
|
318
|
+
)
|
|
319
|
+
assert reopened_verification["finalAnswer"]["items"][0]["answer"] == assessment["authoritativeFinding"]
|
|
320
|
+
locked_verification = (run_dir / "verification.json").read_bytes()
|
|
321
|
+
locked_authority_report = (run_dir / "03-authority-report.html").read_bytes()
|
|
322
|
+
(run_dir / "verification.json").write_bytes(locked_verification + b"\n")
|
|
323
|
+
immutable_rejection = run_failed(command(
|
|
324
|
+
"deliver", "--results", str(results), "--run-dir", str(run_dir)
|
|
325
|
+
))
|
|
326
|
+
assert "第三步权威核验结果已被修改" in immutable_rejection["error"]
|
|
327
|
+
(run_dir / "verification.json").write_bytes(locked_verification)
|
|
328
|
+
delivered = run(command("deliver", "--results", str(results), "--run-dir", str(run_dir)))
|
|
329
|
+
assert delivered["status"] == "completed"
|
|
330
|
+
assert delivered["trustedSearchRequestCount"] == 0 and delivered["dknowExemptCount"] == 1
|
|
331
|
+
assert delivered["answerReferenceReport"] == str((run_dir / "capture" / "report.html").resolve())
|
|
332
|
+
assert delivered["comparisonReport"] == str((run_dir / "comparison.html").resolve())
|
|
333
|
+
assert delivered["artifacts"]["report"] == str((run_dir / "report.html").resolve())
|
|
334
|
+
deliverable_paths = [item["path"] for item in delivered["deliverables"]]
|
|
335
|
+
assert deliverable_paths[:4] == [
|
|
336
|
+
str((run_dir / "01-capture-report.html").resolve()),
|
|
337
|
+
str((run_dir / "02-comparison-report.html").resolve()),
|
|
338
|
+
str((run_dir / "03-authority-report.html").resolve()),
|
|
339
|
+
str((run_dir / "04-final-report.html").resolve()),
|
|
340
|
+
]
|
|
341
|
+
assert deliverable_paths[4] == delivered["artifacts"]["reportPackage"]
|
|
342
|
+
with zipfile.ZipFile(deliverable_paths[4]) as report_archive:
|
|
343
|
+
packaged_checkpoints = json.loads(
|
|
344
|
+
report_archive.read(
|
|
345
|
+
"fact-check-x-report/data/stage-checkpoints.json"
|
|
346
|
+
).decode("utf-8")
|
|
347
|
+
)
|
|
348
|
+
assert list(packaged_checkpoints["stages"]) == [
|
|
349
|
+
"capture", "comparison", "authority", "evaluation"
|
|
350
|
+
]
|
|
351
|
+
assert packaged_checkpoints["stages"]["evaluation"]["status"] == "completed"
|
|
352
|
+
for packaged_stage in packaged_checkpoints["stages"].values():
|
|
353
|
+
for packaged_item in packaged_stage.get("deliverables") or []:
|
|
354
|
+
assert packaged_item["fileUri"].startswith("../")
|
|
355
|
+
assert "file://" not in packaged_item["markdownLink"]
|
|
356
|
+
assert "file://" not in packaged_item["message"]
|
|
357
|
+
assert delivered["checkpoint"]["mustPresentBeforeNextStage"] is True
|
|
358
|
+
assert delivered["checkpoint"]["path"] == str(
|
|
359
|
+
(run_dir / "04-final-report.html").resolve()
|
|
360
|
+
)
|
|
361
|
+
assert "打开各方答案测评报告" in delivered["checkpoint"]["message"]
|
|
362
|
+
assert (run_dir / "verification.json").read_bytes() == locked_verification
|
|
363
|
+
assert (run_dir / "03-authority-report.html").read_bytes() == locked_authority_report
|
|
364
|
+
assert all(Path(item["path"]).exists() for item in delivered["deliverables"])
|
|
365
|
+
for item, label, path in (
|
|
366
|
+
(delivered["deliverables"][0], "各方答案汇总", run_dir / "01-capture-report.html"),
|
|
367
|
+
(delivered["deliverables"][1], "各方答案聚合(未核验)", run_dir / "02-comparison-report.html"),
|
|
368
|
+
(delivered["deliverables"][2], "权威核验后的最终答案", run_dir / "03-authority-report.html"),
|
|
369
|
+
(delivered["deliverables"][3], "各方答案测评报告", run_dir / "04-final-report.html"),
|
|
370
|
+
(delivered["deliverables"][4], "完整可分发报告包", Path(deliverable_paths[4])),
|
|
371
|
+
):
|
|
372
|
+
assert_deliverable(item, label, path)
|
|
373
|
+
manifest = json.loads((run_dir / "pipeline.json").read_text(encoding="utf-8"))
|
|
374
|
+
verification = json.loads((run_dir / "verification.json").read_text(encoding="utf-8"))
|
|
375
|
+
report = (run_dir / "report.html").read_text(encoding="utf-8")
|
|
376
|
+
authority_report = (run_dir / "03-authority-report.html").read_text(
|
|
377
|
+
encoding="utf-8"
|
|
378
|
+
)
|
|
379
|
+
comparison_deliverable = (run_dir / "02-comparison-report.html").read_text(
|
|
380
|
+
encoding="utf-8"
|
|
381
|
+
)
|
|
382
|
+
assert manifest["schemaVersion"] == "fact-check-x/pipeline@2"
|
|
383
|
+
assert verification["finalAnswer"]["status"] == "verified"
|
|
384
|
+
assert verification["finalAnswer"]["knowledgePointIds"] == ["K1"]
|
|
385
|
+
assert verification["knowledgePoints"][0]["authority"]["verdicts"]["doubao"]["category"] == "misleading"
|
|
386
|
+
assert "权威核验报告(问题:" in authority_report
|
|
387
|
+
assert "权威核验后的最终答案" in authority_report
|
|
388
|
+
assert "各平台裁决" not in authority_report
|
|
389
|
+
assert "data-fcx-authority-binding-sha256" not in authority_report
|
|
390
|
+
report_names = [
|
|
391
|
+
"01-capture-report.html",
|
|
392
|
+
"02-comparison-report.html",
|
|
393
|
+
"03-authority-report.html",
|
|
394
|
+
"04-final-report.html",
|
|
395
|
+
]
|
|
396
|
+
for report_name in report_names:
|
|
397
|
+
report_html = (run_dir / report_name).read_text(encoding="utf-8")
|
|
398
|
+
assert report_html.count('data-fcx-report-nav="1"') == 1
|
|
399
|
+
assert 'aria-current="page"' in report_html
|
|
400
|
+
assert "来源链接待补" not in report_html
|
|
401
|
+
assert all(
|
|
402
|
+
f'href="{target}"' in report_html
|
|
403
|
+
for target in report_names
|
|
404
|
+
if target != report_name
|
|
405
|
+
)
|
|
406
|
+
assert all(
|
|
407
|
+
f'href="{target}"' in comparison_deliverable
|
|
408
|
+
for target in (
|
|
409
|
+
"01-capture-report.html",
|
|
410
|
+
"03-authority-report.html",
|
|
411
|
+
"04-final-report.html",
|
|
412
|
+
)
|
|
413
|
+
)
|
|
414
|
+
assert "① 平台表现概览" in report
|
|
415
|
+
assert "② 逐知识点核验明细" in report
|
|
416
|
+
assert "③ 原始答案与参考文献(存证)" in report
|
|
417
|
+
assert "④ 指标口径速查" in report
|
|
418
|
+
assert "⑤ 评测元信息" in report
|
|
419
|
+
assert report.count("② 逐知识点核验明细") == 1
|
|
420
|
+
assert 'data-fcx-locked-role="direct"' in report
|
|
421
|
+
assert 'data-fcx-locked-role="reference"' in report
|
|
422
|
+
assert "② 直接答案逐知识点测评" not in report
|
|
423
|
+
assert "③ 补充参考逐知识点测评" not in report
|
|
424
|
+
assert "② 直接答案逐条判定" not in report
|
|
425
|
+
assert "②-补 补充参考分析" not in report
|
|
426
|
+
assert (run_dir / "comparison.html").exists()
|
|
427
|
+
capture_report = (run_dir / "capture" / "report.html").read_text(encoding="utf-8")
|
|
428
|
+
assert "各方答案汇总(问题:" in capture_report
|
|
429
|
+
assert "每人每月最高提取 1400 元" in capture_report
|
|
430
|
+
assert (run_dir / "capture" / "report.md").exists()
|
|
431
|
+
assert manifest["artifacts"]["answerReferenceReport"] == str((run_dir / "capture" / "report.html").resolve())
|
|
432
|
+
assert manifest["artifacts"]["captureGate"] == str((run_dir / "capture-gate.json").resolve())
|
|
433
|
+
assert manifest["artifacts"]["comparisonAnalysis"] == str(
|
|
434
|
+
(run_dir / "comparison-analysis.json").resolve()
|
|
435
|
+
)
|
|
436
|
+
assert manifest["artifacts"]["authorityGate"] == str((run_dir / "authority-gate.json").resolve())
|
|
437
|
+
|
|
438
|
+
artifact_source = Path(temp) / "artifact-source"
|
|
439
|
+
artifact_source.mkdir()
|
|
440
|
+
artifact_results = json.loads(results.read_text(encoding="utf-8"))
|
|
441
|
+
for platform in artifact_results["platforms"]:
|
|
442
|
+
platform_id = platform["platform"]
|
|
443
|
+
platform["artifacts"] = {
|
|
444
|
+
"screenshot": f"artifacts/{platform_id}/screenshot.png",
|
|
445
|
+
"html": f"artifacts/{platform_id}/page.html",
|
|
446
|
+
}
|
|
447
|
+
artifact_dir = artifact_source / "artifacts" / platform_id
|
|
448
|
+
artifact_dir.mkdir(parents=True)
|
|
449
|
+
(artifact_dir / "screenshot.png").write_bytes(f"png:{platform_id}".encode())
|
|
450
|
+
(artifact_dir / "page.html").write_text(
|
|
451
|
+
f"<html>{platform_id}</html>", encoding="utf-8"
|
|
452
|
+
)
|
|
453
|
+
artifact_results_path = artifact_source / "results.json"
|
|
454
|
+
artifact_results_path.write_text(
|
|
455
|
+
json.dumps(artifact_results, ensure_ascii=False), encoding="utf-8"
|
|
456
|
+
)
|
|
457
|
+
(artifact_source / "capture-recovery.json").write_text(
|
|
458
|
+
json.dumps({
|
|
459
|
+
"schemaVersion": "fact-check-x/capture-recovery@2",
|
|
460
|
+
"status": "completed",
|
|
461
|
+
}, ensure_ascii=False),
|
|
462
|
+
encoding="utf-8",
|
|
463
|
+
)
|
|
464
|
+
artifact_run = Path(temp) / "artifact-run"
|
|
465
|
+
run(command(
|
|
466
|
+
"prepare-comparison",
|
|
467
|
+
"--results",
|
|
468
|
+
str(artifact_results_path),
|
|
469
|
+
"--run-dir",
|
|
470
|
+
str(artifact_run),
|
|
471
|
+
))
|
|
472
|
+
artifact_gate = json.loads(
|
|
473
|
+
(artifact_run / "capture-gate.json").read_text(encoding="utf-8")
|
|
474
|
+
)
|
|
475
|
+
assert len(artifact_gate["artifactSha256"]) == 4
|
|
476
|
+
assert artifact_gate["recoverySha256"]
|
|
477
|
+
assert (artifact_run / "capture/capture-recovery.json").is_file()
|
|
478
|
+
for platform in artifact_results["platforms"]:
|
|
479
|
+
platform_id = platform["platform"]
|
|
480
|
+
assert (
|
|
481
|
+
artifact_run / f"capture/artifacts/{platform_id}/screenshot.png"
|
|
482
|
+
).is_file()
|
|
483
|
+
assert (
|
|
484
|
+
artifact_run / f"capture/artifacts/{platform_id}/page.html"
|
|
485
|
+
).is_file()
|
|
486
|
+
assert (
|
|
487
|
+
artifact_run / f"artifacts/{platform_id}/screenshot.png"
|
|
488
|
+
).is_file()
|
|
489
|
+
assert (
|
|
490
|
+
artifact_run / f"artifacts/{platform_id}/page.html"
|
|
491
|
+
).is_file()
|
|
492
|
+
missing_artifact_results = json.loads(
|
|
493
|
+
artifact_results_path.read_text(encoding="utf-8")
|
|
494
|
+
)
|
|
495
|
+
(
|
|
496
|
+
artifact_source
|
|
497
|
+
/ missing_artifact_results["platforms"][0]["artifacts"]["screenshot"]
|
|
498
|
+
).unlink()
|
|
499
|
+
missing_artifact_run = Path(temp) / "missing-artifact-run"
|
|
500
|
+
missing_artifact = run_failed(command(
|
|
501
|
+
"prepare-comparison",
|
|
502
|
+
"--results",
|
|
503
|
+
str(artifact_results_path),
|
|
504
|
+
"--run-dir",
|
|
505
|
+
str(missing_artifact_run),
|
|
506
|
+
))
|
|
507
|
+
assert "存证文件不存在" in missing_artifact["error"]
|
|
508
|
+
|
|
509
|
+
configuration_run = Path(temp) / "configuration-run"
|
|
510
|
+
run(command("prepare-comparison", "--results", str(results), "--run-dir", str(configuration_run)))
|
|
511
|
+
configuration_analysis = json.loads((COMPARE_FIXTURES / "comparison-analysis.json").read_text(encoding="utf-8"))
|
|
512
|
+
configuration_analysis["knowledgePoints"][0]["trustedAnchor"] = {"eligible": False}
|
|
513
|
+
configuration_analysis["knowledgePoints"][0]["claims"]["dknowc-chat"] = {
|
|
514
|
+
"covered": False,
|
|
515
|
+
"claim": "",
|
|
516
|
+
"answerExcerpt": "",
|
|
517
|
+
"citedReferenceIndexes": [],
|
|
518
|
+
"answerLevelReferenceIndexes": [],
|
|
519
|
+
"faithfulness": "insufficient",
|
|
520
|
+
"reason": "",
|
|
521
|
+
"evidence": [],
|
|
522
|
+
}
|
|
523
|
+
configuration_analysis_path = configuration_run / "comparison-analysis.json"
|
|
524
|
+
configuration_analysis_path.write_text(json.dumps(configuration_analysis, ensure_ascii=False), encoding="utf-8")
|
|
525
|
+
run(command("complete-comparison", "--results", str(results), "--analysis", str(configuration_analysis_path), "--run-dir", str(configuration_run)))
|
|
526
|
+
configuration_required = run_failed(command(
|
|
527
|
+
"prepare-authority",
|
|
528
|
+
"--run-dir",
|
|
529
|
+
str(configuration_run),
|
|
530
|
+
), keyless_environment)
|
|
531
|
+
assert configuration_required["status"] == "configuration_required"
|
|
532
|
+
assert configuration_required["action"] == "configure_trusted_search"
|
|
533
|
+
assert configuration_required["trustedSearchRequiredCount"] == 1
|
|
534
|
+
assert "您只需完成登录" in configuration_required["userPrompt"]
|
|
535
|
+
assert "自动读取已有的可用 Key" in configuration_required["userPrompt"]
|
|
536
|
+
assert configuration_required["configuration"]["providerUrl"] == "https://platform.dknowc.cn/auth/#/login"
|
|
537
|
+
assert configuration_required["configuration"]["interaction"] == "browser_login_only"
|
|
538
|
+
assert configuration_required["configuration"]["requiresChatSecret"] is False
|
|
539
|
+
assert configuration_required["configuration"]["autoResume"] is True
|
|
540
|
+
assert configuration_required["configuration"]["sharedAcrossCarriers"] is True
|
|
541
|
+
assert configuration_required["configuration"]["command"][-1] == "configure"
|
|
542
|
+
assert configuration_required["configuration"]["command"][-2].endswith(
|
|
543
|
+
"trusted_search_config.py"
|
|
544
|
+
)
|
|
545
|
+
assert json.loads((configuration_run / "authority-gate.json").read_text(encoding="utf-8"))["status"] == "configuration_required"
|
|
546
|
+
search_blocked = run_failed(command(
|
|
547
|
+
"search-authority",
|
|
548
|
+
"--run-dir",
|
|
549
|
+
str(configuration_run),
|
|
550
|
+
"--max-workers",
|
|
551
|
+
"12",
|
|
552
|
+
), keyless_environment)
|
|
553
|
+
assert search_blocked["status"] == "configuration_required"
|
|
554
|
+
assert search_blocked["action"] == "configure_trusted_search"
|
|
555
|
+
assert not (configuration_run / "authority" / "evidence" / "batch.json").exists()
|
|
556
|
+
bypass_attempt = run_failed(command(
|
|
557
|
+
"finalize-authority",
|
|
558
|
+
"--run-dir",
|
|
559
|
+
str(configuration_run),
|
|
560
|
+
), keyless_environment)
|
|
561
|
+
assert bypass_attempt["status"] == "failed"
|
|
562
|
+
assert "禁止写入裁决" in bypass_attempt["error"]
|
|
563
|
+
bypass_delivery = run_failed(command(
|
|
564
|
+
"deliver",
|
|
565
|
+
"--results",
|
|
566
|
+
str(results),
|
|
567
|
+
"--run-dir",
|
|
568
|
+
str(configuration_run),
|
|
569
|
+
), keyless_environment)
|
|
570
|
+
assert bypass_delivery["status"] == "failed"
|
|
571
|
+
assert "缺少 authority 阶段产物交付记录" in bypass_delivery["error"]
|
|
572
|
+
|
|
573
|
+
tamper_run = Path(temp) / "tamper-run"
|
|
574
|
+
run(command("prepare-comparison", "--results", str(results), "--run-dir", str(tamper_run)))
|
|
575
|
+
run(command("complete-comparison", "--results", str(results), "--analysis", str(COMPARE_FIXTURES / "comparison-analysis.json"), "--run-dir", str(tamper_run)))
|
|
576
|
+
tampered = json.loads((tamper_run / "comparison.json").read_text(encoding="utf-8"))
|
|
577
|
+
tampered["knowledgePoints"][0]["claims"]["doubao"]["sourceLevel"] = "official"
|
|
578
|
+
(tamper_run / "comparison.json").write_text(json.dumps(tampered, ensure_ascii=False), encoding="utf-8")
|
|
579
|
+
tamper_rejected = run_failed(command("prepare-authority", "--run-dir", str(tamper_run)))
|
|
580
|
+
assert "被修改" in tamper_rejected["error"]
|
|
581
|
+
|
|
582
|
+
pollution_run = Path(temp) / "pollution-run"
|
|
583
|
+
run(command("prepare-comparison", "--results", str(results), "--run-dir", str(pollution_run)))
|
|
584
|
+
run(command("complete-comparison", "--results", str(results), "--analysis", str(COMPARE_FIXTURES / "comparison-analysis.json"), "--run-dir", str(pollution_run)))
|
|
585
|
+
run(command("prepare-authority", "--run-dir", str(pollution_run)))
|
|
586
|
+
run(command("search-authority", "--run-dir", str(pollution_run), "--max-workers", "12"))
|
|
587
|
+
pollution_assessments = pollution_run / "authority" / "assessments"
|
|
588
|
+
pollution_assessments.mkdir(parents=True)
|
|
589
|
+
shutil.copyfile(AUTHORITY_FIXTURES / "K1-assessment.json", pollution_assessments / "K1.json")
|
|
590
|
+
pollution_results = pollution_run / "authority" / "results"
|
|
591
|
+
pollution_results.mkdir(parents=True)
|
|
592
|
+
(pollution_results / "KP-001.json").write_text("{}\n", encoding="utf-8")
|
|
593
|
+
pollution_rejected = run_failed(command("finalize-authority", "--run-dir", str(pollution_run)))
|
|
594
|
+
assert "results 目录必须为空" in pollution_rejected["error"]
|
|
595
|
+
|
|
596
|
+
invalid_assessments = Path(temp) / "invalid-assessments"
|
|
597
|
+
invalid_assessments.mkdir()
|
|
598
|
+
(invalid_assessments / "K1.json").write_text(json.dumps({
|
|
599
|
+
"requestId": "K1",
|
|
600
|
+
"verdict": "supported",
|
|
601
|
+
"officialAnswer": "旧结构结论",
|
|
602
|
+
"platformAssessment": {
|
|
603
|
+
"dknowc-chat": {"verdict": "supported", "reason": "旧结构"},
|
|
604
|
+
"doubao": {"verdict": "supported", "reason": "旧结构"},
|
|
605
|
+
},
|
|
606
|
+
}, ensure_ascii=False), encoding="utf-8")
|
|
607
|
+
invalid_run = Path(temp) / "invalid-run"
|
|
608
|
+
run(command("prepare-comparison", "--results", str(results), "--run-dir", str(invalid_run)))
|
|
609
|
+
run(command("complete-comparison", "--results", str(results), "--analysis", str(COMPARE_FIXTURES / "comparison-analysis.json"), "--run-dir", str(invalid_run)))
|
|
610
|
+
run(command("prepare-authority", "--run-dir", str(invalid_run)))
|
|
611
|
+
run(command("search-authority", "--run-dir", str(invalid_run), "--max-workers", "12"))
|
|
612
|
+
invalid_final = run_failed(command(
|
|
613
|
+
"finalize-authority",
|
|
614
|
+
"--run-dir",
|
|
615
|
+
str(invalid_run),
|
|
616
|
+
"--assessments-dir",
|
|
617
|
+
str(invalid_assessments),
|
|
618
|
+
))
|
|
619
|
+
assert invalid_final["status"] == "failed"
|
|
620
|
+
assert "裁决文件结构错误" in invalid_final["error"]
|
|
621
|
+
assert not (invalid_run / "authority" / "results" / "K1.json").exists()
|
|
622
|
+
|
|
623
|
+
review_run = Path(temp) / "review-run"
|
|
624
|
+
run(command("prepare-comparison", "--results", str(results), "--run-dir", str(review_run)))
|
|
625
|
+
run(command("complete-comparison", "--results", str(results), "--analysis", str(COMPARE_FIXTURES / "comparison-analysis.json"), "--run-dir", str(review_run)))
|
|
626
|
+
run(command("prepare-authority", "--run-dir", str(review_run)))
|
|
627
|
+
run(command("search-authority", "--run-dir", str(review_run), "--max-workers", "12"))
|
|
628
|
+
review_assessments = review_run / "authority" / "assessments"
|
|
629
|
+
review_assessments.mkdir(parents=True)
|
|
630
|
+
review_assessment = json.loads((AUTHORITY_FIXTURES / "K1-assessment.json").read_text(encoding="utf-8"))
|
|
631
|
+
review_assessment["verdicts"]["doubao"] = {
|
|
632
|
+
"verdict": "insufficient",
|
|
633
|
+
"reason": "当前证据不足以完成裁决",
|
|
634
|
+
"evidenceIds": [],
|
|
635
|
+
}
|
|
636
|
+
(review_assessments / "K1.json").write_text(json.dumps(review_assessment, ensure_ascii=False), encoding="utf-8")
|
|
637
|
+
review_final = run(command("finalize-authority", "--run-dir", str(review_run)))
|
|
638
|
+
assert review_final["status"] == "completed"
|
|
639
|
+
assert review_final["stage"] == "authority_completed"
|
|
640
|
+
assert review_final["evidenceGapCount"] == 1
|
|
641
|
+
assert review_final["deliverables"][0]["path"] == str(
|
|
642
|
+
(review_run / "03-authority-report.html").resolve()
|
|
643
|
+
)
|
|
644
|
+
review_authority_report = (
|
|
645
|
+
review_run / "03-authority-report.html"
|
|
646
|
+
).read_text(encoding="utf-8")
|
|
647
|
+
assert "证据不足项" in review_authority_report
|
|
648
|
+
assert 'href="04-final-report.html"' in review_authority_report
|
|
649
|
+
assert json.loads((review_run / "authority-gate.json").read_text(encoding="utf-8"))["status"] == "finalized"
|
|
650
|
+
review_delivery = run(command("deliver", "--results", str(results), "--run-dir", str(review_run)))
|
|
651
|
+
assert review_delivery["status"] == "completed"
|
|
652
|
+
assert review_delivery["evidenceGapCount"] == 1
|
|
653
|
+
assert (review_run / "04-final-report.html").is_file()
|
|
654
|
+
review_verification = json.loads(
|
|
655
|
+
(review_run / "verification.json").read_text(encoding="utf-8")
|
|
656
|
+
)
|
|
657
|
+
assert review_verification["status"] == "completed"
|
|
658
|
+
assert review_verification["evidenceGapCount"] == 1
|
|
659
|
+
assert review_verification["evidenceGaps"][0]["platform"] == "doubao"
|
|
660
|
+
|
|
661
|
+
failed_results = json.loads(results.read_text(encoding="utf-8"))
|
|
662
|
+
failed_results["platforms"][0]["answerMarkdown"] = "为您智能匹配到当前所在区域为“北京市”,如想咨询其他区域可点击修改"
|
|
663
|
+
failed_results["platforms"][1]["status"] = "failed"
|
|
664
|
+
failed_results["platforms"][1]["answerMarkdown"] = ""
|
|
665
|
+
failed_results["platforms"][1]["error"] = "No answer text detected."
|
|
666
|
+
failed_path = Path(temp) / "failed-results.json"
|
|
667
|
+
failed_path.write_text(json.dumps(failed_results, ensure_ascii=False), encoding="utf-8")
|
|
668
|
+
failed_run = Path(temp) / "failed-run"
|
|
669
|
+
rejected = run_failed(command("prepare-comparison", "--results", str(failed_path), "--run-dir", str(failed_run)))
|
|
670
|
+
assert rejected["status"] == "failed"
|
|
671
|
+
assert "禁止进入知识点对比" in rejected["error"]
|
|
672
|
+
assert not (failed_run / "comparison-task.json").exists()
|
|
673
|
+
|
|
674
|
+
answered_with_region_footer = json.loads(results.read_text(encoding="utf-8"))
|
|
675
|
+
answered_with_region_footer["platforms"][0]["answerMarkdown"] = (
|
|
676
|
+
"北京市高考报名资格、网上申请、填报缴费和现场确认的完整政策回答。" * 10
|
|
677
|
+
+ "页面底部:为您智能匹配到当前所在区域为“北京市”,如想咨询其他区域可点击修改"
|
|
678
|
+
)
|
|
679
|
+
answered_with_region_footer_path = Path(temp) / "answered-with-region-footer.json"
|
|
680
|
+
answered_with_region_footer_path.write_text(
|
|
681
|
+
json.dumps(answered_with_region_footer, ensure_ascii=False),
|
|
682
|
+
encoding="utf-8",
|
|
683
|
+
)
|
|
684
|
+
answered_with_region_footer_run = Path(temp) / "answered-with-region-footer-run"
|
|
685
|
+
accepted_with_footer = run(command(
|
|
686
|
+
"prepare-comparison",
|
|
687
|
+
"--results",
|
|
688
|
+
str(answered_with_region_footer_path),
|
|
689
|
+
"--run-dir",
|
|
690
|
+
str(answered_with_region_footer_run),
|
|
691
|
+
))
|
|
692
|
+
assert accepted_with_footer["stage"] == "capture_completed"
|
|
693
|
+
assert (answered_with_region_footer_run / "comparison-task.json").exists()
|
|
694
|
+
|
|
695
|
+
recovery_capture = Path(temp) / "recovery-capture"
|
|
696
|
+
recovery_capture.mkdir()
|
|
697
|
+
recovery_results = recovery_capture / "results.json"
|
|
698
|
+
recovery_results.write_text(results.read_text(encoding="utf-8"), encoding="utf-8")
|
|
699
|
+
(recovery_capture / "capture-recovery.json").write_text(
|
|
700
|
+
json.dumps({
|
|
701
|
+
"schemaVersion": "fact-check-x/capture-recovery@2",
|
|
702
|
+
"status": "required",
|
|
703
|
+
"action": "computer_use",
|
|
704
|
+
}, ensure_ascii=False),
|
|
705
|
+
encoding="utf-8",
|
|
706
|
+
)
|
|
707
|
+
recovery_run = Path(temp) / "recovery-run"
|
|
708
|
+
recovery_rejected = run_failed(command(
|
|
709
|
+
"prepare-comparison",
|
|
710
|
+
"--results",
|
|
711
|
+
str(recovery_results),
|
|
712
|
+
"--run-dir",
|
|
713
|
+
str(recovery_run),
|
|
714
|
+
))
|
|
715
|
+
assert "Computer Use" in recovery_rejected["error"]
|
|
716
|
+
assert not (recovery_run / "comparison-task.json").exists()
|
|
717
|
+
assert not (ROOT / "assets").exists()
|
|
718
|
+
if os.getenv("FACT_CHECK_X_ASSERTIONS_OUTPUT"):
|
|
719
|
+
Path(os.environ["FACT_CHECK_X_ASSERTIONS_OUTPUT"]).write_text(json.dumps({
|
|
720
|
+
"schemaVersion": "fact-check-x/test-assertions@1",
|
|
721
|
+
"actualAssertionIds": [
|
|
722
|
+
"report.stage_artifacts_visible",
|
|
723
|
+
"report.checkpoints_indexed",
|
|
724
|
+
"report.unverified_draft_visible",
|
|
725
|
+
"report.verified_final_answer_visible",
|
|
726
|
+
"report.stage2_role_sections",
|
|
727
|
+
"report.stage4_single_locked_detail",
|
|
728
|
+
"report.technical_failure_notice",
|
|
729
|
+
"report.renamed_four_stages",
|
|
730
|
+
"authority.finalize_transaction_rollback",
|
|
731
|
+
"comparison.overwide_claim_blocks_authority",
|
|
732
|
+
"path.windows_drive_normalized",
|
|
733
|
+
],
|
|
734
|
+
}), encoding="utf-8")
|
|
735
|
+
print("PASS Fact-Check-X 统一入口")
|
|
736
|
+
return 0
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
if __name__ == "__main__":
|
|
740
|
+
raise SystemExit(main())
|