Setting the file. One moment. Retrieval Verify · Foundry Iq · microsoft/azure-skills · Skills DocsFile Cu Canary
This file
- Number
- 10.63
- Position
- 63 of 77
- Type
- Python
- Size
- 20 KB
- Lines
- 418
helpers/retrieval_verify.py
Python·418 lines·20 KB
import
Any
12
13MAX_RESPONSE_BYTES = 5 * 1024 * 1024
14MAX_INPUT_BYTES = 1024 * 1024
15MAX_DEPTH = 48
16MAX_ITEMS = 1000
17VERSIONS = {"2026-04-01", "2026-08-01-preview"}
18
19
20class EvidenceError(ValueError):
21 pass
22
23
24def require(condition: bool, code: str) -> None:
25 if not condition:
26 raise EvidenceError(code)
27
28
29def _pairs(pairs: list[tuple[str, Any]]) -> dict:
30 result = {}
31 for key, value in pairs:
32 require(key not in result, "duplicate-json-key")
33 result[key] = value
34 return result
35
36
37def parse(text: str) -> Any:
38 # Bound nesting before the JSON decoder allocates containers or recurses.
39 depth, quoted, escaped = 0, False, False
40 for char in text:
41 if quoted:
42 if escaped:
43 escaped = False
44 elif char == "\\":
45 escaped = True
46 elif char == '"':
47 quoted = False
48 elif char == '"':
49 quoted = True
50 elif char in "[{":
51 depth += 1
52 require(depth <= MAX_DEPTH, "json-depth-exceeded")
53 elif char in "]}":
54 depth -= 1
55 try:
56 return json.loads(text, object_pairs_hook=_pairs,
57 parse_constant=lambda _: require(False, "invalid-json-number"))
58 except (ValueError, RecursionError) as exc:
59 if isinstance(exc, EvidenceError):
60 raise
61 raise EvidenceError("invalid-json") from None
62
63
64def load(path: Path, limit: int) -> Any:
65 try:
66 require(path.is_file() and not path.is_symlink(), "evidence-not-regular-file")
67 with path.open("rb") as stream:
68 payload = stream.read(limit + 1)
69 require(len(payload) <= limit, "evidence-too-large")
70 return parse(payload.decode("utf-8"))
71 except (OSError, UnicodeError):
72 raise EvidenceError("evidence-unreadable") from None
73
74
75def obj(value: Any, fields: set[str] | None = None) -> dict:
76 require(isinstance(value, dict), "object-required")
77 if fields is not None:
78 require(not (value.keys() - fields), "unknown-input-field")
79 return value
80
81
82def seq(value: Any) -> list:
83 require(isinstance(value, list) and len(value) <= MAX_ITEMS, "array-missing-or-oversized")
84 return value
85
86
87def text(value: Any) -> str:
88 require(isinstance(value, str) and bool(value.strip()), "nonempty-text-required")
89 return value
90
91
92def timestamp(value: Any) -> datetime:
93 try:
94 result = datetime.fromisoformat(text(value).replace("Z", "+00:00"))
95 require(result.tzinfo is not None, "timestamp-zone-required")
96 return result
97 except ValueError as exc:
98 if isinstance(exc, EvidenceError):
99 raise
100 raise EvidenceError("invalid-timestamp") from None
101
102
103def evidence_path(root: Path, value: Any) -> Path:
104 raw = text(value)
105 require(not re.match(r"^[a-zA-Z][a-zA-Z0-9+.\-]*:", raw.lstrip())
106 and not raw.lstrip().startswith(("//", "\\\\")),
107 "evidence-path-url-not-supported")
108 relative = Path(raw)
109 require(not relative.is_absolute() and ".." not in relative.parts,
110 "evidence-path-outside-bundle")
111 path = root / relative
112 require(path.resolve().is_relative_to(root.resolve()), "evidence-path-outside-bundle")
113 require(not any(p.is_symlink() for p in [path, *path.parents] if p != root.parent),
114 "evidence-link-not-supported")
115 return path
116
117
118class Audit:
119 def __init__(self) -> None:
120 self.findings: list[dict] = []
121 self.diagnostics: list[dict] = []
122 self.statuses: set[str] = set()
123 self.snapshot_equal: bool | None = None
124 self.cases: dict[str, dict] = {}
125 self.first_failure: dict | None = None
126
127 def add(self, code: str, where: str, status: str = "failed") -> None:
128 self.statuses.add(status)
129 if status == "failed" and self.first_failure is None:
130 self.first_failure = {"code": code, "at": where, "status": status}
131 if len(self.findings) < 40:
132 self.findings.append({"code": code, "at": where, "status": status})
133
134 def diagnostic(self, value: Any, where: str, kind: str) -> None:
135 # Retain exact first error/warning in the input artifact, not in public output.
136 digest = hashlib.sha256(json.dumps(value, sort_keys=True).encode()).hexdigest()
137 self.diagnostics.append({"at": where, "kind": kind, "sha256": digest})
138 self.add("activity-" + kind, where, "failed" if kind == "error" else "unverified")
139
140 def check_diagnostic(self, container: dict, field: str, where: str) -> None:
141 if field not in container or container[field] is None:
142 return
143 value = container[field]
144 if not isinstance(value, dict if field == "error" else str):
145 self.add("diagnostic-shape-invalid", where)
146 elif field == "error" or value != "":
147 self.diagnostic(value, where, field)
148
149 def result(self) -> dict:
150 statuses = self.statuses
151 structural = "failed" if "failed" in statuses else (
152 "unverified" if "unverified" in statuses else "passed")
153 return {
154 "status": "blocked" if structural == "failed" else "review-required",
155 "outcome": "retained-retrieval-audit", "structural_status": structural,
156 "overall_pass": False, "mutation": "none", "writes_performed": [],
157 "network_calls": 0, "model_calls": 0, "cleanup": "not-applicable",
158 "before_after_snapshot_equal": self.snapshot_equal,
159 "cases": self.cases,
160 "findings": self.findings,
161 "diagnostic_count": len(self.diagnostics), "diagnostics": self.diagnostics[:20],
162 "first_issue": self.findings[0] if self.findings else None,
163 "first_failure": self.first_failure,
164 "first_error": next((d for d in self.diagnostics if d["kind"] == "error"), None),
165 "first_warning": next((d for d in self.diagnostics if d["kind"] == "warning"), None),
166 "unverified": ["semantic-answer-faithfulness", "semantic-abstention",
167 "live-evidence-provenance", "agent-invocation-proof"],
168 }
169
170
171def snapshot(value: Any, document: dict) -> dict:
172 value = obj(value, {"captured_at", "endpoint", "api_version", "knowledge_base",
173 "knowledge_source", "original"})
174 require(value.get("endpoint") == document["endpoint"]
175 and value.get("api_version") == document["api_version"], "snapshot-target-mismatch")
176 kb, source = obj(value.get("knowledge_base")), obj(value.get("knowledge_source"))
177 require(kb.get("name") == document["knowledge_base_name"]
178 and source.get("name") == document["source_name"]
179 and source.get("kind") == document["source_kind"], "snapshot-resource-mismatch")
180 text(kb.get("@odata.etag"))
181 text(source.get("@odata.etag"))
182 parameters = obj(source.get("fileParameters" if source["kind"] == "file"
183 else "azureBlobParameters"))
184 obj(parameters.get("ingestionParameters"))
185 if source["kind"] == "azureBlob":
186 text(parameters.get("containerName"))
187 require(kb.get("knowledgeSources") == [{"name": document["source_name"]}],
188 "snapshot-source-selection-unsupported")
189 obj(value.get("original"))
190 timestamp(value.get("captured_at"))
191 return value
192
193
194def inspect_response(audit: Audit, retained: Any, mode: str, label: str,
195 document: dict, original: dict) -> None:
196 retained = obj(retained)
197 if "status" in retained and retained["status"] not in ("response-received", "blocked"):
198 audit.add("retained-status-unsupported", label + "/status")
199 return
200 if "operation" in retained and retained["operation"] != "retrieve":
201 audit.add("retained-operation-unsupported", label + "/operation")
202 return
203 status = retained.get("http_status")
204 if retained.get("status") == "blocked":
205 if not isinstance(retained.get("first_blocker"), dict):
206 audit.add("retained-blocker-invalid", label + "/first_blocker")
207 else:
208 audit.diagnostic(retained["first_blocker"], label + "/first_blocker", "error")
209 return
210 if "first_blocker" in retained:
211 audit.add("retained-blocker-without-blocked-status", label + "/first_blocker")
212 return
213 require(type(status) is int and 100 <= status <= 599, "http-status-missing-or-invalid")
214 summary = {"http_status": status, "original_path_matches": 0,
215 "declared_identity_value_matches": 0, "original_identity": "unverified",
216 "original_identity_requirement": "fileId" if document["source_kind"] == "file" else "etag"}
217 audit.cases[label] = summary
218 if status != 200:
219 audit.add("http-status-" + str(status), label + "/http_status")
220 body = obj(retained.get("response_body"))
221 audit.check_diagnostic(body, "error", label + "/response_body/error")
222 activities = {}
223 for i, activity in enumerate(seq(body.get("activity"))):
224 activity = obj(activity)
225 aid = activity.get("id")
226 require(type(aid) is int and aid >= 0 and aid not in activities, "invalid-activity-id")
227 activities[aid] = activity
228 for field in ("error", "warning"):
229 audit.check_diagnostic(activity, field, f"{label}/response_body/activity/{i}/{field}")
230 if "knowledgeSourceName" in activity and (
231 activity["knowledgeSourceName"] != document["source_name"]
232 or activity.get("type") != document["source_kind"]
233 ):
234 audit.add("extraneous-source-activity", label)
235 if not activities:
236 audit.add("activity-evidence-missing", label, "unverified")
237 if not any(a.get("knowledgeSourceName") == document["source_name"]
238 and a.get("type") == document["source_kind"] for a in activities.values()):
239 audit.add("selected-source-activity-missing", label, "unverified")
240 references = {}
241 expected = document["expected_source"]
242 for i, reference in enumerate(seq(body.get("references"))):
243 reference = obj(reference)
244 rid = text(reference.get("id"))
245 require(rid not in references, "duplicate-reference-id")
246 references[rid] = reference
247 where = f"{label}/response_body/references/{i}"
248 aid = reference.get("activitySource")
249 require(type(aid) is int, "invalid-reference-activity-id")
250 activity = activities.get(aid, {})
251 if activity.get("knowledgeSourceName") != document["source_name"]:
252 audit.add("reference-source-unverified", where, "unverified")
253 if reference.get("type") != document["source_kind"]:
254 audit.add("citation-type-unsupported", where)
255 continue
256 native_path = "docName" if document["source_kind"] == "file" else "blobUrl"
257 if reference.get(native_path) != expected["path"]:
258 audit.add("original-path-missing-or-mismatch", where)
259 else:
260 summary["original_path_matches"] += 1
261 # Native references do not promise a File ID or Blob ETag. Do not use
262 # docKey, citationUrl, ref_id, sourceData.id, or chunk IDs as substitutes.
263 binding = expected.get("identity_field")
264 source_data = reference.get("sourceData")
265 identity_key = "fileId" if document["source_kind"] == "file" else "etag"
266 audit.add("original-identity-not-in-native-citation", where, "unverified")
267 if binding:
268 audit.add("operator-identity-binding-unverified", where, "unverified")
269 if not isinstance(source_data, dict) or binding not in source_data:
270 audit.add("declared-identity-value-missing", where, "unverified")
271 elif source_data[binding] != original[identity_key]:
272 audit.add("declared-identity-value-mismatch", where)
273 else:
274 summary["declared_identity_value_matches"] += 1
275
276 used: list[str] = []
277 extracts = 0
278 blocks = 0
279 for message in seq(body.get("response")):
280 for content in seq(obj(message).get("content")):
281 content = obj(content)
282 require(content.get("type") == "text", "response-content-type-unsupported")
283 value = text(content.get("text"))
284 blocks += 1
285 if mode == "extractiveData":
286 rows = seq(parse(value))
287 extracts += len(rows)
288 for row in rows:
289 row = obj(row)
290 used.append(text(row.get("ref_id")))
291 if not any(isinstance(v, str) and v.strip()
292 for k, v in row.items() if k != "ref_id"):
293 audit.add("extract-payload-missing", label)
294 else:
295 markers = re.findall(r"\[ref_id:([^\]\r\n]+)\]", value)
296 if value.count("[ref_id:") != len(markers):
297 audit.add("malformed-citation-marker", label)
298 used.extend(markers)
299 if not blocks:
300 audit.add("response-payload-missing", label, "unverified")
301 if len(used) > MAX_ITEMS:
302 raise EvidenceError("citation-count-exceeded")
303 if mode == "extractiveData" and len(used) != len(set(used)):
304 audit.add("duplicate-extract-reference-id", label)
305 if set(used) - references.keys():
306 audit.add("unknown-citation-id", label)
307 if label == "customer" and not used:
308 audit.add("customer-grounding-missing", label)
309 if label == "unrelated":
310 if mode == "extractiveData" and (extracts or references):
311 audit.add("unrelated-support-returned", label)
312 elif mode == "answerSynthesis" and used:
313 audit.add("unrelated-answer-citations-require-review", label, "unverified")
314 elif mode == "answerSynthesis":
315 audit.add("no-citations-is-not-semantic-abstention", label, "unverified")
316 summary.update(response_blocks=blocks, extracts=extracts if mode == "extractiveData" else None,
317 references=len(references), used_citations=len(used),
318 no_support_shape=(blocks > 0 and not extracts and not references)
319 if mode == "extractiveData" else "semantic-review-required")
320
321
322def verify(document: Any, root: Path, *, now: datetime | None = None) -> dict:
323 audit = Audit()
324 where = "input"
325 try:
326 document = obj(document, {"schema_version", "endpoint", "api_version",
327 "knowledge_base_name", "source_name", "source_kind",
328 "expected_source", "before", "after", "customer", "unrelated"})
329 require(document.get("schema_version") == "1.0", "unsupported-schema")
330 require(text(document.get("api_version")) in VERSIONS, "unsupported-api")
331 require(text(document.get("source_kind")) in {"file", "azureBlob"}, "unsupported-source")
332 for name in ("endpoint", "knowledge_base_name", "source_name"):
333 text(document.get(name))
334 expected = obj(document.get("expected_source"), {"path", "file_id", "etag",
335 "identity_field"})
336 text(expected.get("path"))
337 if "identity_field" in expected:
338 binding = text(expected["identity_field"])
339 require(binding not in {"id", "docKey", "ref_id", "chunk_id", "chunkId"},
340 "chunk-identity-binding-forbidden")
341 before = snapshot(load(evidence_path(root, document.get("before")), MAX_INPUT_BYTES), document)
342 after = snapshot(load(evidence_path(root, document.get("after")), MAX_INPUT_BYTES), document)
343 start, end = timestamp(before["captured_at"]), timestamp(after["captured_at"])
344 now = now or datetime.now(timezone.utc)
345 require(start <= end <= now and (now - start).total_seconds() <= 3600,
346 "stale-or-unordered-snapshots")
347 audit.snapshot_equal = all(before[key] == after[key] for key in (
348 "knowledge_base", "knowledge_source", "original"))
349 for key in ("knowledge_base", "knowledge_source", "original"):
350 if before[key] != after[key]:
351 audit.add("before-after-definition-or-original-changed", key)
352 original = before["original"]
353 if document["source_kind"] == "file":
354 require(original.get("fileName") == expected["path"]
355 and text(original.get("fileId")) == text(expected.get("file_id"))
356 and "errorMessage" in original and original["errorMessage"] is None,
357 "original-file-inventory-mismatch")
358 require(timestamp(original.get("lastUpdatedAt")) <= start,
359 "original-version-after-snapshot")
360 else:
361 require(original.get("url") == expected["path"]
362 and text(original.get("etag")) == text(expected.get("etag")),
363 "original-blob-inventory-mismatch")
364 kb = before["knowledge_base"]
365 if document["api_version"] == "2026-04-01":
366 require("outputMode" not in kb and "retrievalReasoningEffort" not in kb,
367 "ga-preview-fields-not-supported")
368 require(document["source_kind"] == "azureBlob" and not obj(
369 before["knowledge_source"].get("azureBlobParameters", {})
370 ).get("isADLSGen2"), "unsupported-ga-source")
371 mode, effort = "extractiveData", "minimal"
372 else:
373 mode = kb.get("outputMode")
374 effort = obj(kb.get("retrievalReasoningEffort")).get("kind")
375 require(mode in {"extractiveData", "answerSynthesis"}
376 and effort in {"minimal", "low", "medium"}, "unsupported-mode")
377 if mode == "answerSynthesis" or effort != "minimal":
378 require(bool(seq(kb.get("models"))), "model-definition-missing")
379 queries = []
380 for label in ("customer", "unrelated"):
381 where = label
382 case = obj(document.get(label), {"query", "approved", "basis", "request",
383 "response", "captured_at"})
384 query = text(case.get("query"))
385 queries.append(query)
386 require(case.get("approved") is True, "question-not-approved")
387 require(case.get("basis") in ({"title", "content"} if label == "customer"
388 else {"unrelated"}), "question-basis-invalid")
389 require(start <= timestamp(case.get("captured_at")) <= end,
390 "response-outside-snapshot-window")
391 request = ({"intents": [{"type": "semantic", "search": query}]} if effort == "minimal"
392 else {"messages": [{"role": "user", "content": [{"type": "text", "text": query}]}]})
393 require(case.get("request") == request, "retained-request-mismatch-or-override")
394 retained = load(evidence_path(root, case.get("response")), MAX_RESPONSE_BYTES)
395 inspect_response(audit, retained, mode, label, document, original)
396 require(queries[0] != queries[1], "paired-questions-must-differ")
397 except (ValueError, OSError, KeyError, TypeError, RecursionError) as exc:
398 code = str(exc) if isinstance(exc, EvidenceError) else "evidence-schema-invalid"
399 audit.add(code, where)
400 return audit.result()
401
402
403def main(argv: list[str] | None = None) -> int:
404 parser = argparse.ArgumentParser(description=__doc__)
405 parser.add_argument("--input", required=True, type=Path)
406 args = parser.parse_args(argv)
407 try:
408 result = verify(load(args.input, MAX_INPUT_BYTES), args.input.parent)
409 except EvidenceError as exc:
410 audit = Audit()
411 audit.add(str(exc), "input")
412 result = audit.result()
413 print(json.dumps(result, sort_keys=True))
414 return 2 if result["status"] == "blocked" else 0
415
416
417if __name__ == "__main__":
418 sys.exit(main())