| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056 |
- from __future__ import annotations
- import re
- from copy import deepcopy
- from typing import Any
- from ..card_details import CardBusinessDataProjector, CardDataNotFound
- from ..card_details.common import find_detail
- from ..inspector_projection import project_activity_detail, project_event_detail
- from ..module_audit.log_context import locate_log_module
- from ..retrieval_detail_projection import (
- QUERY_TOOL_NAMES,
- RETRIEVAL_RESULT_COLLECTION_KEYS,
- )
- from .comparison import align_business_projection, business_only_projection
- from .resolver import SourceResolver, pointer_value
- from .schema import SourceBinding, unresolved_binding
- class InspectorViewNotFound(LookupError):
- pass
- class InspectorWorkbenchProjector:
- """Application service that makes all three Inspector columns together."""
- def __init__(self) -> None:
- self._card_projector = CardBusinessDataProjector()
- def related_event_ids(
- self, detail_ref: str, *, bundle: dict[str, Any], view: dict[str, Any]
- ) -> list[int]:
- ref = str(detail_ref or "")
- ids: set[int] = set()
- if ref.startswith("event:"):
- value = _suffix_int(ref)
- if value:
- ids.add(value)
- node = find_detail(view, ref)
- _collect_event_ids(node, ids)
- if ref == "run:objective":
- _collect_event_ids(view.get("objective"), ids)
- round_match = re.fullmatch(r"round:(\d+):(goal|plan|result|evaluation)", ref)
- if round_match:
- round_index = int(round_match.group(1))
- round_ = next(
- (
- item
- for item in view.get("rounds") or []
- if _integer(item.get("roundIndex")) == round_index
- ),
- {},
- )
- role = round_match.group(2)
- target = {
- "goal": round_.get("goal"),
- "plan": (round_.get("plan") or {}).get("node"),
- "result": round_.get("overallEvaluation"),
- "evaluation": round_.get("overallEvaluation"),
- }.get(role)
- _collect_event_ids(target, ids)
- event_names = {
- "goal": {"begin_round"},
- "plan": {"record_multipath_plan"},
- }.get(role, set())
- for event in bundle.get("events") or []:
- if (
- event.get("event_name") in event_names
- and (
- role == "goal"
- or _integer(event.get("round_index")) == round_index
- )
- ):
- ids.add(_integer(event.get("id")) or 0)
- branch_match = re.fullmatch(
- r"round:(\d+):branch:(\d+):(task|retrieval|output)", ref
- )
- if branch_match:
- round_index, branch_id = int(branch_match.group(1)), int(branch_match.group(2))
- branch = _view_branch(view, round_index, branch_id)
- _collect_event_ids(branch, ids)
- if ref.startswith("creative:"):
- seed_id = _suffix_int(ref)
- seed = _bundle_event(bundle, seed_id)
- round_index = _integer((seed or {}).get("round_index"))
- branch_id = _integer((seed or {}).get("branch_id"))
- implementers = {
- _integer(event.get("id"))
- for event in bundle.get("events") or []
- if event.get("event_name") == "script_implementer"
- and _integer(event.get("round_index")) == round_index
- and _integer(event.get("branch_id")) == branch_id
- }
- for event in bundle.get("events") or []:
- if _integer(event.get("round_index")) != round_index or _integer(event.get("branch_id")) != branch_id:
- continue
- event_id = _integer(event.get("id"))
- if event_id in implementers or _integer(event.get("scope_event_id")) in implementers:
- ids.add(event_id or 0)
- if ref.startswith("multipath-decision:"):
- decision_id = _suffix_int(ref)
- decision = next(
- (
- item
- for item in bundle.get("multipathDecisions") or []
- if _integer(item.get("id")) == decision_id
- ),
- {},
- )
- round_index = _integer(decision.get("round_index"))
- for event in bundle.get("events") or []:
- if (
- event.get("event_name") == "script_multipath_evaluator"
- and _integer(event.get("round_index")) == round_index
- ):
- ids.add(_integer(event.get("id")) or 0)
- if ref.startswith("retrieval-agent:"):
- agent_id = _suffix_int(ref)
- for event in bundle.get("events") or []:
- if (
- _integer(event.get("id")) == agent_id
- or _integer(event.get("parent_event_id")) == agent_id
- or _integer(event.get("scope_event_id")) == agent_id
- ):
- ids.add(_integer(event.get("id")) or 0)
- return sorted(value for value in ids if value > 0)
- def project(
- self,
- detail_ref: str,
- *,
- script_build_id: int,
- bundle: dict[str, Any],
- view: dict[str, Any],
- event_details: dict[int, dict[str, Any]],
- log_contents: dict[int, str] | None = None,
- ) -> dict[str, Any]:
- ref = str(detail_ref or "").strip()
- def event_loader(event_id: int) -> dict[str, Any]:
- event = event_details.get(int(event_id))
- if event is None:
- raise KeyError(event_id)
- return event
- business = self._business_detail(
- ref,
- script_build_id=script_build_id,
- bundle=bundle,
- view=view,
- event_details=event_details,
- )
- try:
- card_data = self._card_projector.project(
- ref,
- bundle=bundle,
- view=view,
- load_event=event_loader,
- )
- except (CardDataNotFound, KeyError):
- card_data = _fallback_card_data(ref, business)
- resolver = SourceResolver(bundle=bundle, event_details=event_details)
- source_modules = self._modules(ref, business, card_data, resolver, event_details, bundle)
- if not source_modules:
- raise InspectorViewNotFound(f"未找到活动 {ref}")
- alignment_kind = (
- "retrieval-event"
- if business.get("detailKind") == "retrieval-event"
- else str(card_data.get("cardKind") or _fallback_card_kind(ref))
- )
- modules = align_business_projection(
- detail_ref=ref,
- card_kind=alignment_kind,
- business=business,
- source_modules=source_modules,
- resolver=resolver,
- bundle=bundle,
- )
- if not modules:
- raise InspectorViewNotFound(f"活动 {ref} 没有可展示的业务详情")
- if log_contents:
- log_refs: dict[int, str] = {}
- for event_id, event in event_details.items():
- log_module = locate_log_module(log_contents.get(event_id) or "", event)
- if log_module:
- log_refs[event_id] = resolver.add_log_anchor(
- event_id,
- log_module,
- event_msg_id=str(event.get("msg_id") or ""),
- )
- for module in modules:
- runtime_refs = list(module.get("runtimeRefs") or [])
- for event_id, log_ref in log_refs.items():
- if f"event:{event_id}" in runtime_refs and log_ref not in runtime_refs:
- runtime_refs.append(log_ref)
- module["runtimeRefs"] = runtime_refs
- completeness = str(card_data.get("completeness") or "partial")
- if any(
- binding.get("resolution") != "resolved"
- for binding in _all_bindings(modules)
- ):
- completeness = "partial" if completeness != "missing" else "missing"
- notices = [
- *[item for item in card_data.get("notices") or [] if isinstance(item, dict)],
- *_business_notices(business),
- ]
- used_source_ids = _used_source_ids(modules)
- return {
- "schemaVersion": "inspector-source-v2",
- "detailRef": ref,
- "cardKind": card_data.get("cardKind") or _fallback_card_kind(ref),
- "completeness": completeness,
- "businessProjection": business_only_projection(business),
- "modules": modules,
- "sources": {
- source_id: source
- for source_id, source in resolver.sources.items()
- if source_id in used_source_ids
- },
- "notices": _unique_notices(notices),
- }
- def _business_detail(
- self,
- detail_ref: str,
- *,
- script_build_id: int,
- bundle: dict[str, Any],
- view: dict[str, Any],
- event_details: dict[int, dict[str, Any]],
- ) -> dict[str, Any]:
- if detail_ref.startswith("event:"):
- event_id = _suffix_int(detail_ref)
- event = event_details.get(event_id or -1)
- if event is None:
- raise InspectorViewNotFound(f"未找到运行事件 {event_id}")
- try:
- return project_activity_detail(
- script_build_id,
- detail_ref,
- view,
- bundle,
- event_detail=event,
- )
- except KeyError:
- return project_event_detail(
- script_build_id,
- event,
- run_status=((view.get("header") or {}).get("status")),
- )
- event_detail = None
- if detail_ref.startswith("retrieval-agent:"):
- run = find_detail(view, detail_ref) or {}
- event_detail = event_details.get(_integer(run.get("eventId")) or -1)
- try:
- return project_activity_detail(
- script_build_id,
- detail_ref,
- view,
- bundle,
- event_detail=event_detail,
- )
- except KeyError as exc:
- raise InspectorViewNotFound(f"未找到活动 {detail_ref}") from exc
- def _modules(
- self,
- detail_ref: str,
- business: dict[str, Any],
- card_data: dict[str, Any],
- resolver: SourceResolver,
- event_details: dict[int, dict[str, Any]],
- bundle: dict[str, Any],
- ) -> list[dict[str, Any]]:
- if detail_ref.startswith("retrieval-direct:"):
- return _direct_group_modules(card_data, resolver, event_details)
- if detail_ref.startswith("retrieval-agent:"):
- return _retrieval_agent_modules(card_data, resolver, event_details)
- special = self._event_modules(detail_ref, business, resolver, event_details)
- if special is not None:
- return special
- business_values = _business_modules(business)
- planned = [
- item
- for item in (card_data.get("displayUse") or {}).get("businessModules") or []
- if isinstance(item, dict)
- ]
- fields = {
- str(item.get("id")): item
- for item in [
- *(card_data.get("businessInputs") or []),
- *(card_data.get("businessOutputs") or []),
- ]
- if isinstance(item, dict) and item.get("id")
- }
- if card_data.get("cardKind") == "implementation-task" and not any(
- str((field.get("source") or {}).get("kind") or "") == "runtime-event"
- for field in fields.values()
- ):
- return _historical_implementation_task_modules(fields, resolver)
- modules: list[dict[str, Any]] = []
- consumed: set[str] = set()
- for index, plan in enumerate(planned, 1):
- module_id = str(plan.get("id") or f"module-{index}")
- title = str(plan.get("title") or f"业务模块 {index}")
- business_module = _match_business_module(business_values, module_id, title)
- if business_module:
- consumed.add(str(business_module.get("id")))
- source_ids = [str(value) for value in plan.get("sourceIds") or []]
- bindings = [
- resolver.binding_for_field(
- fields[source_id],
- binding_id=f"{module_id}:source:{position}",
- )
- for position, source_id in enumerate(source_ids, 1)
- if source_id in fields
- ]
- if card_data.get("cardKind") == "implementation-task" and business_module:
- task_field = next(
- (
- fields[source_id]
- for source_id in source_ids
- if source_id in fields
- and str((fields[source_id].get("source") or {}).get("kind")) == "runtime-event"
- and str((fields[source_id].get("source") or {}).get("fieldPath")) == "input.content.task"
- ),
- None,
- )
- task_event_id = _suffix_int(str((task_field or {}).get("source", {}).get("ref") or ""))
- if task_event_id and _module_value(business_module):
- bindings = [
- resolver.text_span_binding(
- task_event_id,
- binding_id=f"{module_id}:task-span",
- path="input.content.task",
- exact_text=_module_value(business_module),
- role="input",
- ),
- *[
- binding
- for binding in bindings
- if binding.get("sourceId") != f"event:{task_event_id}"
- ],
- ]
- aggregate = _aggregate_binding(
- str(card_data.get("cardKind") or ""),
- module_id,
- source_ids,
- fields,
- resolver,
- bundle,
- )
- if aggregate is not None:
- bindings = [aggregate]
- if module_id == "process" and not bindings:
- bindings = [
- resolver.binding_for_event(
- event_id,
- binding_id=f"{module_id}:event:{event_id}",
- role="process",
- )
- for event_id in sorted(event_details)
- ]
- if card_data.get("cardKind") == "multipath-decision" and module_id == "outcomes":
- for binding in bindings:
- source_record = resolver.sources.get(str(binding.get("sourceId") or "")) or {}
- raw_record = source_record.get("rawRecord")
- branch_status = str((raw_record or {}).get("status") or "").lower() if isinstance(raw_record, dict) else ""
- if branch_status in {"merged", "adopted", "accepted", "selected"}:
- binding["evidence"]["adoption"] = "explicitly-adopted"
- binding["evidence"]["confidence"] = "exact"
- elif branch_status in {"discarded", "rejected", "not_adopted"}:
- binding["evidence"]["adoption"] = "explicitly-rejected"
- binding["evidence"]["confidence"] = "exact"
- if not bindings:
- binding = unresolved_binding(
- f"{module_id}:unresolved",
- reason=f"模块「{title}」没有保存可安全定位的原始记录",
- code="binding-map-missing",
- )
- resolver.add_unresolved_source(binding)
- bindings = [binding]
- value = _module_value(business_module)
- if value in (None, "", [], {}) and source_ids:
- values = [fields[source_id].get("value") for source_id in source_ids if source_id in fields]
- value = values[0] if len(values) == 1 else values
- _align_transforms_with_business(value, bindings, resolver, module_id)
- module = {
- "id": module_id,
- "title": title,
- "presentation": _presentation(business_module, title),
- "business": {"value": value},
- "bindings": bindings,
- "runtimeRefs": _runtime_refs(bindings),
- "defaultOpen": index <= 3,
- }
- if business_module and business_module.get("items"):
- business_items = _normalize_items(business_module.get("items") or [])
- item_rows = []
- child_bindings: list[SourceBinding] = []
- for item_index, item in enumerate(business_items, 1):
- item_bindings = (
- [_multipath_comparison_binding(
- item,
- item_index,
- bindings,
- resolver,
- bundle,
- )]
- if card_data.get("cardKind") == "multipath-decision" and module_id == "comparison"
- else _item_bindings(
- module_id,
- item_index,
- item,
- len(business_items),
- bindings,
- resolver,
- )
- )
- child_bindings.extend(item_bindings)
- item_rows.append(
- {
- "id": f"{module_id}:item:{item_index}",
- "label": item.get("label"),
- "value": item.get("value"),
- "bindings": item_bindings,
- }
- )
- module["business"] = {"items": item_rows}
- module["bindings"] = _unique_bindings(child_bindings)
- module["runtimeRefs"] = _runtime_refs(module["bindings"])
- elif business_module and business_module.get("steps"):
- step_rows = []
- step_bindings: list[SourceBinding] = []
- for step_index, step in enumerate(business_module.get("steps") or [], 1):
- if not isinstance(step, dict):
- continue
- step_event_id = _suffix_int(str(step.get("eventRef") or ""))
- if step_event_id and step_event_id in event_details:
- step_binding = resolver.binding_for_event(
- step_event_id,
- binding_id=f"{module_id}:step:{step_index}",
- path="",
- role="process",
- )
- step_binding["transform"] = {
- "kind": "parsed",
- "operation": "creative-step-v1",
- "outputKey": str(step_index),
- }
- step_binding["evidence"]["confidence"] = "deterministic"
- else:
- step_binding = unresolved_binding(
- f"{module_id}:step:{step_index}:unresolved",
- reason=f"创作步骤 {step_index} 没有保存可安全关联的 Event",
- source_id=(f"event:{step_event_id}" if step_event_id else None),
- resolution="unsafe",
- code="unsafe-association",
- )
- if step_binding["sourceId"] not in resolver.sources:
- resolver.add_unresolved_source(step_binding)
- step_bindings.append(step_binding)
- step_rows.append({
- "id": f"{module_id}:step:{step_index}",
- "label": step.get("action") or f"步骤 {step_index}",
- "value": step,
- "bindings": [step_binding],
- })
- module["business"] = {"items": step_rows}
- module["bindings"] = step_bindings
- module["runtimeRefs"] = _runtime_refs(step_bindings)
- modules.append(module)
- # The formal Inspector exposes only modules declared by the unified
- # card contract. Legacy business blocks are an input adapter, not a
- # second set of UI modules; appending unmatched blocks here created
- # duplicates such as Goal + “本轮目标构成” with no trustworthy binding.
- return modules
- def _event_modules(
- self,
- detail_ref: str,
- business: dict[str, Any],
- resolver: SourceResolver,
- event_details: dict[int, dict[str, Any]],
- ) -> list[dict[str, Any]] | None:
- if not detail_ref.startswith("event:"):
- return None
- event_id = _suffix_int(detail_ref)
- event = event_details.get(event_id or -1)
- if event is None:
- return None
- if business.get("detailKind") == "retrieval-event":
- return _retrieval_event_modules(business, event, resolver)
- if event.get("event_name") == "script_implementer":
- input_side = event.get("input") if isinstance(event.get("input"), dict) else {}
- content = input_side.get("content") if isinstance(input_side, dict) else None
- task = content.get("task") if isinstance(content, dict) else content
- sections = {
- str(item.get("id")): item
- for item in business.get("businessSections") or []
- if isinstance(item, dict)
- }
- action_binding = resolver.binding_for_event(
- event_id or 0,
- binding_id="action:event",
- path="event_name",
- role="process",
- )
- action_binding["transform"] = {
- "kind": "parsed",
- "operation": "implementation-activity-label-v1",
- "outputKey": "action",
- }
- action_binding["evidence"]["confidence"] = "deterministic"
- request_binding = resolver.binding_for_event(
- event_id or 0,
- binding_id="request:event",
- path="input.content.task",
- role="input",
- availability="direct-read",
- )
- request_binding["transform"] = {
- "kind": "parsed",
- "operation": "implementation-task-readable-sections-v1",
- "outputKey": "request",
- }
- request_binding["evidence"]["confidence"] = "deterministic"
- result_value = (sections.get("result") or {}).get("content")
- result_binding = resolver.text_span_binding(
- event_id or 0,
- binding_id="result:event",
- path="output.content.summary",
- exact_text=result_value,
- role="output",
- )
- return [
- {
- "id": "action",
- "title": "做了什么",
- "presentation": "text",
- "business": {"value": (sections.get("action") or {}).get("content")},
- "bindings": [action_binding],
- "runtimeRefs": [detail_ref],
- "defaultOpen": True,
- },
- {
- "id": "request",
- "title": "处理内容",
- "presentation": "text",
- "business": {"value": (sections.get("request") or {}).get("content")},
- "bindings": [request_binding],
- "runtimeRefs": [detail_ref],
- "defaultOpen": True,
- },
- {
- "id": "result",
- "title": "得到什么",
- "presentation": "text",
- "business": {"value": result_value},
- "bindings": [result_binding],
- "runtimeRefs": [detail_ref],
- "defaultOpen": True,
- },
- ]
- values = _business_modules(business)
- planning_paths = {
- "summary": "input.content.thought_summary",
- "thought": "input.content.thought",
- "plan": "input.content.plan",
- "action": "input.content.action",
- }
- modules: list[dict[str, Any]] = []
- is_planning = (
- event.get("event_name") == "think_and_plan"
- and event.get("agent_role") == "main"
- and _integer(event.get("agent_depth")) == 0
- )
- is_review = event.get("event_name") in {"script_multipath_evaluator", "script_evaluator"}
- if not is_planning and not is_review:
- return _generic_event_modules(
- detail_ref, business, event, resolver, event_id or 0
- )
- for index, item in enumerate(values, 1):
- module_id = str(item.get("id") or f"event-module-{index}")
- title = str(item.get("title") or f"运行模块 {index}")
- value = _module_value(item)
- input_title = title in {"评审范围", "评审标准", "评审对象", "标准"}
- if is_planning and module_id in planning_paths:
- binding = resolver.binding_for_event(
- event_id or 0,
- binding_id=f"{module_id}:event:{event_id}",
- path=planning_paths[module_id],
- role="input",
- availability="direct-read",
- )
- elif title == "记录说明" and not event.get("output"):
- binding = resolver.binding_for_event(
- event_id or 0,
- binding_id=f"{module_id}:event:{event_id}",
- role="status",
- availability="produced-by-run",
- )
- binding["transform"] = {
- "kind": "calculated",
- "operation": "runtime-missing-decision-notice-v1",
- }
- binding["evidence"]["confidence"] = "deterministic"
- else:
- side = "input" if input_title else "output"
- binding = resolver.text_span_binding(
- event_id or 0,
- binding_id=f"{module_id}:event:{event_id}",
- path=_event_text_path(event, side),
- exact_text=value,
- role="input" if input_title else "output",
- )
- business_payload: dict[str, Any] = {"value": value}
- bindings = [binding]
- if item.get("items"):
- rows = []
- bindings = []
- for item_index, child in enumerate(_normalize_items(item.get("items") or []), 1):
- child_bindings = []
- for leaf_index, leaf in enumerate(_leaf_texts(child.get("value")), 1):
- child_bindings.append(resolver.text_span_binding(
- event_id or 0,
- binding_id=f"{module_id}:item:{item_index}:leaf:{leaf_index}",
- path=_event_text_path(event, "input" if input_title else "output"),
- exact_text=leaf,
- role="input" if input_title else "output",
- ))
- if not child_bindings:
- fallback = resolver.binding_for_event(
- event_id or 0,
- binding_id=f"{module_id}:item:{item_index}:record",
- path=_event_text_path(event, "input" if input_title else "output"),
- role="input" if input_title else "output",
- availability="direct-read" if input_title else "returned-to-agent",
- )
- fallback["transform"] = {
- "kind": "parsed",
- "operation": "review-item-v1",
- "outputKey": str(item_index),
- }
- fallback["evidence"]["confidence"] = "deterministic"
- child_bindings = [fallback]
- bindings.extend(child_bindings)
- rows.append(
- {
- "id": f"{module_id}:item:{item_index}",
- "label": child.get("label"),
- "value": child.get("value"),
- "bindings": child_bindings,
- }
- )
- business_payload = {"items": rows}
- modules.append(
- {
- "id": module_id,
- "title": title,
- "presentation": _presentation(item, title),
- "business": business_payload,
- "bindings": bindings,
- "runtimeRefs": [detail_ref],
- "defaultOpen": index <= 3,
- }
- )
- return modules
- def _generic_event_modules(
- detail_ref: str,
- business: dict[str, Any],
- event: dict[str, Any],
- resolver: SourceResolver,
- event_id: int,
- ) -> list[dict[str, Any]] | None:
- """Bind the readable action/request/result projection to the real Event Body."""
- sections = [
- item
- for item in business.get("businessSections") or []
- if isinstance(item, dict)
- ]
- if not sections:
- return None
- modules: list[dict[str, Any]] = []
- for index, section in enumerate(sections, 1):
- module_id = str(section.get("id") or f"section-{index}")
- value = section.get("content")
- if module_id == "action":
- path, role, availability = "event_name", "process", "produced-by-run"
- operation = "runtime-activity-label-v1"
- elif module_id == "request":
- path, role, availability = _event_text_path(event, "input"), "input", "direct-read"
- operation = "runtime-request-readable-projection-v1"
- elif module_id == "result":
- path, role, availability = _event_text_path(event, "output"), "output", "returned-to-agent"
- operation = "runtime-result-readable-projection-v1"
- else:
- # The section is visibly derived from this Event, but there is no
- # card-specific field contract. Bind the complete Event instead of
- # falsely claiming that its original record is missing.
- path, role, availability = "", "process", "produced-by-run"
- operation = "runtime-business-section-v1"
- binding = resolver.binding_for_event(
- event_id,
- binding_id=f"{module_id}:event:{event_id}",
- path=path,
- role=role,
- availability=availability,
- )
- binding["transform"] = {
- "kind": "parsed",
- "operation": operation,
- "outputKey": module_id,
- }
- binding["evidence"]["confidence"] = "deterministic"
- modules.append({
- "id": module_id,
- "title": str(section.get("title") or f"业务模块 {index}"),
- "presentation": "text",
- "business": {"value": value},
- "bindings": [binding],
- "runtimeRefs": [detail_ref],
- "defaultOpen": True,
- })
- return modules
- def _retrieval_event_modules(
- business: dict[str, Any], event: dict[str, Any], resolver: SourceResolver
- ) -> list[dict[str, Any]]:
- event_id = _integer(event.get("id")) or 0
- conditions = business.get("queryConditions") or []
- condition_items = []
- condition_bindings: list[SourceBinding] = []
- for index, item in enumerate(conditions, 1):
- key = str(item.get("key") or index)
- binding = resolver.binding_for_event(
- event_id,
- binding_id=f"purpose:{key}",
- path=f"input.content.{key}",
- role="input",
- availability="direct-read",
- )
- condition_bindings.append(binding)
- condition_items.append(
- {
- "id": f"purpose:item:{key}",
- "label": item.get("label"),
- "value": item.get("value"),
- "bindings": [binding],
- }
- )
- if not condition_bindings:
- binding = resolver.binding_for_event(
- event_id,
- binding_id="purpose:event-input",
- path="input.content",
- role="input",
- availability="direct-read",
- )
- condition_bindings = [binding]
- name = str(event.get("event_name") or "")
- result_key = RETRIEVAL_RESULT_COLLECTION_KEYS.get(name)
- root = "output.content" + (f".{result_key}" if result_key else "")
- root_value = pointer_value(event, f"/{root.replace('.', '/')}")
- result_items = []
- result_bindings: list[SourceBinding] = []
- for index, item in enumerate(business.get("items") or [], 1):
- item_path = (
- f"{root}.{index - 1}"
- if isinstance(root_value, list)
- else root
- )
- binding = resolver.binding_for_event(
- event_id,
- binding_id=f"result:item:{index}",
- path=item_path,
- role="output",
- availability="returned-to-agent",
- )
- binding["transform"] = {
- "kind": "parsed",
- "operation": "retrieval-result-item-v1",
- "outputKey": str(index - 1),
- }
- binding["evidence"]["confidence"] = "deterministic"
- result_bindings.append(binding)
- result_items.append(
- {
- "id": f"result:item:{index}",
- "label": item.get("title"),
- "value": item,
- "bindings": [binding],
- }
- )
- if not result_bindings:
- empty_result = resolver.binding_for_event(
- event_id,
- binding_id="result:event-output",
- path="output.content",
- role="output",
- availability="returned-to-agent",
- )
- empty_result["transform"] = {
- "kind": "parsed",
- "operation": "retrieval-empty-or-failure-message-v1",
- "outputKey": "message",
- }
- empty_result["evidence"]["confidence"] = "deterministic"
- result_bindings = [empty_result]
- status_bindings = _event_status_bindings(
- resolver, event_id, prefix="status", include_output=True
- )
- event_source = resolver.sources.get(f"event:{event_id}")
- if event_source is not None and str((business.get("outcome") or {}).get("state")) == "empty":
- event_source["resultState"] = "empty"
- return [
- {
- "id": "purpose",
- "title": "目的 / 查询条件",
- "presentation": "items",
- "business": {"items": condition_items} if condition_items else {"value": "未记录调用输入"},
- "bindings": condition_bindings,
- "runtimeRefs": [f"event:{event_id}"],
- "defaultOpen": True,
- },
- {
- "id": "result",
- "title": "返回内容 / 结果列表",
- "presentation": "items",
- "business": {"items": result_items} if result_items else {"value": (business.get("outcome") or {}).get("message")},
- "bindings": result_bindings,
- "runtimeRefs": [f"event:{event_id}"],
- "defaultOpen": True,
- },
- {
- "id": "status",
- "title": "状态、耗时与完整性",
- "presentation": "notice",
- "business": {"value": {"outcome": business.get("outcome"), "completeness": business.get("completeness")}},
- "bindings": status_bindings,
- "runtimeRefs": [f"event:{event_id}"],
- "defaultOpen": True,
- },
- ]
- def _direct_group_modules(
- card_data: dict[str, Any],
- resolver: SourceResolver,
- event_details: dict[int, dict[str, Any]],
- ) -> list[dict[str, Any]]:
- events = [event_details[event_id] for event_id in sorted(event_details)]
- def rows(kind: str) -> tuple[list[dict[str, Any]], list[SourceBinding]]:
- values: list[dict[str, Any]] = []
- bindings: list[SourceBinding] = []
- for index, event in enumerate(events, 1):
- event_id = _integer(event.get("id")) or 0
- if kind == "input":
- path, role, availability = "input.content", "input", "direct-read"
- label = str(event.get("event_name") or f"调用 {index}")
- value = ((event.get("input") or {}).get("content") if isinstance(event.get("input"), dict) else None)
- elif kind == "output":
- path, role, availability = "output.content", "output", "returned-to-agent"
- label = str(event.get("event_name") or f"调用 {index}")
- value = ((event.get("output") or {}).get("content") if isinstance(event.get("output"), dict) else None)
- else:
- path, role, availability = "status", "status", "produced-by-run"
- label = str(event.get("event_name") or f"调用 {index}")
- value = {
- "status": event.get("status"),
- "durationMs": event.get("duration_ms"),
- "error": event.get("error_message"),
- }
- item_bindings = (
- _event_status_bindings(resolver, event_id, prefix=kind)
- if kind == "status"
- else [resolver.binding_for_event(
- event_id,
- binding_id=f"{kind}:event:{event_id}",
- path=path,
- role=role,
- availability=availability,
- )]
- )
- bindings.extend(item_bindings)
- values.append(
- {
- "id": f"{kind}:item:{event_id}",
- "label": label,
- "value": value,
- "bindings": item_bindings,
- }
- )
- return values, bindings
- input_rows, input_bindings = rows("input")
- output_rows, output_bindings = rows("output")
- status_rows, status_bindings = rows("status")
- failed_rows = [
- row
- for row in status_rows
- if str((row.get("value") or {}).get("status") or "").lower()
- in {"failed", "failure", "error", "interrupted"}
- ]
- failed_ids = {row["id"].rsplit(":", 1)[-1] for row in failed_rows}
- failed_bindings = [
- binding
- for binding in status_bindings
- if binding["id"].rsplit(":", 1)[-1] in failed_ids
- ]
- if not failed_rows:
- failed_bindings = [resolver.members_binding(
- {
- "id": "failures:none",
- "label": "失败调用数量",
- "value": 0,
- "relation": "persisted-output",
- },
- binding_id="failures:none-calculated",
- members=[{
- "id": f"failures:status:{event.get('id')}",
- "label": str(event.get("event_name") or event.get("id")),
- "value": event.get("status"),
- "source": {
- "kind": "runtime-event",
- "label": str(event.get("event_name") or f"Event {event.get('id')}"),
- "ref": f"event:{event.get('id')}",
- "fieldPath": "status",
- },
- "relation": "persisted-output",
- "completeness": "complete",
- } for event in events],
- reducer="no-failed-tool-status-v1",
- role="status",
- )]
- return [
- {
- "id": "purpose",
- "title": "读取目的",
- "presentation": "items",
- "business": {"items": input_rows},
- "bindings": input_bindings,
- "runtimeRefs": _runtime_refs(input_bindings),
- "defaultOpen": True,
- },
- {
- "id": "calls",
- "title": "逐次工具调用",
- "presentation": "items",
- "business": {"items": status_rows},
- "bindings": status_bindings,
- "runtimeRefs": _runtime_refs(status_bindings),
- "defaultOpen": True,
- },
- {
- "id": "results",
- "title": "实际返回的信息",
- "presentation": "items",
- "business": {"items": output_rows},
- "bindings": output_bindings,
- "runtimeRefs": _runtime_refs(output_bindings),
- "defaultOpen": True,
- },
- {
- "id": "failures",
- "title": "失败与缺失",
- "presentation": "notice",
- "business": {"items": failed_rows} if failed_rows else {"value": "未发现失败或中断的工具调用。"},
- "bindings": failed_bindings,
- "runtimeRefs": _runtime_refs(failed_bindings),
- "defaultOpen": bool(failed_rows),
- },
- ]
- def _retrieval_agent_modules(
- card_data: dict[str, Any],
- resolver: SourceResolver,
- event_details: dict[int, dict[str, Any]],
- ) -> list[dict[str, Any]]:
- events = [event_details[event_id] for event_id in sorted(event_details)]
- agent = next(
- (event for event in events if str(event.get("event_type") or "") == "agent_invoke"),
- events[0] if events else {},
- )
- agent_id = _integer(agent.get("id")) or 0
- children = [event for event in events if _integer(event.get("id")) != agent_id]
- queries = [
- event
- for event in children
- if str(event.get("event_name") or "") in QUERY_TOOL_NAMES
- ]
- task_binding = resolver.binding_for_event(
- agent_id,
- binding_id="task:agent-input",
- path="input.content.task",
- role="input",
- availability="direct-read",
- )
- screening_binding = resolver.binding_for_event(
- agent_id,
- binding_id="screening:agent-output",
- path="output.content",
- role="output",
- availability="produced-by-run",
- )
- task = ((agent.get("input") or {}).get("content") if isinstance(agent.get("input"), dict) else None)
- task = task.get("task") if isinstance(task, dict) else task
- screening = ((agent.get("output") or {}).get("content") if isinstance(agent.get("output"), dict) else None)
- query_rows: list[dict[str, Any]] = []
- result_rows: list[dict[str, Any]] = []
- status_rows: list[dict[str, Any]] = []
- query_bindings: list[SourceBinding] = []
- result_bindings: list[SourceBinding] = []
- status_bindings: list[SourceBinding] = []
- for index, event in enumerate(queries, 1):
- event_id = _integer(event.get("id")) or 0
- label = str(event.get("event_name") or f"查询 {index}")
- input_binding = resolver.binding_for_event(
- event_id,
- binding_id=f"queries:event:{event_id}",
- path="input.content",
- role="input",
- availability="direct-read",
- )
- output_binding = resolver.binding_for_event(
- event_id,
- binding_id=f"raw:event:{event_id}",
- path="output.content",
- role="output",
- availability="returned-to-agent",
- )
- status_item_bindings = _event_status_bindings(
- resolver, event_id, prefix="failures"
- )
- query_bindings.append(input_binding)
- result_bindings.append(output_binding)
- status_bindings.extend(status_item_bindings)
- query_rows.append({
- "id": f"queries:item:{event_id}", "label": label,
- "value": ((event.get("input") or {}).get("content") if isinstance(event.get("input"), dict) else None),
- "bindings": [input_binding],
- })
- result_rows.append({
- "id": f"raw:item:{event_id}", "label": label,
- "value": ((event.get("output") or {}).get("content") if isinstance(event.get("output"), dict) else None),
- "bindings": [output_binding],
- })
- status_rows.append({
- "id": f"failures:item:{event_id}", "label": label,
- "value": {"status": event.get("status"), "error": event.get("error_message")},
- "bindings": status_item_bindings,
- })
- if not queries:
- query_bindings = [
- _calculation_binding(
- resolver,
- binding_id="queries:empty",
- label="取数 Agent 查询事件计数",
- value=[],
- role="process",
- )
- ]
- result_bindings = [
- _calculation_binding(
- resolver,
- binding_id="raw:empty",
- label="取数 Agent 原始命中计数",
- value=[],
- role="output",
- )
- ]
- status_bindings = [
- _calculation_binding(
- resolver,
- binding_id="failures:empty",
- label="取数 Agent 失败查询计数",
- value=[],
- role="status",
- )
- ]
- activity_rows: list[dict[str, Any]] = []
- activity_bindings: list[SourceBinding] = []
- for event in events:
- event_id = _integer(event.get("id")) or 0
- binding = resolver.binding_for_event(
- event_id,
- binding_id=f"activities:event:{event_id}",
- role="process",
- )
- activity_bindings.append(binding)
- activity_rows.append(
- {
- "id": f"activities:item:{event_id}",
- "label": str(event.get("event_name") or f"Event {event_id}"),
- "value": {
- "eventName": event.get("event_name"),
- "eventType": event.get("event_type"),
- "status": event.get("status"),
- },
- "bindings": [binding],
- }
- )
- return [
- {
- "id": "task", "title": "完整取数任务", "presentation": "text",
- "business": {"value": task}, "bindings": [task_binding],
- "runtimeRefs": [f"event:{agent_id}"], "defaultOpen": True,
- },
- {
- "id": "queries", "title": "查询过程", "presentation": "items",
- "business": {"items": query_rows}, "bindings": query_bindings,
- "runtimeRefs": _runtime_refs(query_bindings), "defaultOpen": True,
- },
- {
- "id": "raw", "title": "原始命中", "presentation": "items",
- "business": {"items": result_rows}, "bindings": result_bindings,
- "runtimeRefs": _runtime_refs(result_bindings), "defaultOpen": True,
- },
- {
- "id": "screening", "title": "初筛整理", "presentation": "text",
- "business": {"value": screening}, "bindings": [screening_binding],
- "runtimeRefs": [f"event:{agent_id}"], "defaultOpen": True,
- },
- {
- "id": "failures", "title": "失败与空结果", "presentation": "items",
- "business": {"items": status_rows}, "bindings": status_bindings,
- "runtimeRefs": _runtime_refs(status_bindings), "defaultOpen": False,
- },
- {
- "id": "activities", "title": "运行记录", "presentation": "process",
- "business": {"items": activity_rows}, "bindings": activity_bindings,
- "runtimeRefs": _runtime_refs(activity_bindings), "defaultOpen": False,
- },
- ]
- def _calculation_binding(
- resolver: SourceResolver,
- *,
- binding_id: str,
- label: str,
- value: Any,
- role: str,
- ) -> SourceBinding:
- binding = resolver.binding_for_field(
- {
- "id": binding_id,
- "label": label,
- "value": value,
- "source": {
- "kind": "calculation",
- "label": label,
- "ref": f"calculation:{binding_id}",
- "fieldPath": "value",
- },
- "relation": "persisted-output",
- "completeness": "complete",
- },
- binding_id=binding_id,
- role=role,
- )
- binding["evidence"]["confidence"] = "deterministic"
- return binding
- def _event_status_bindings(
- resolver: SourceResolver,
- event_id: int,
- *,
- prefix: str,
- include_output: bool = False,
- ) -> list[SourceBinding]:
- """Bind every Event value rendered by a status/diagnostic module."""
- bindings = [
- resolver.binding_for_event(
- event_id,
- binding_id=f"{prefix}:status:event:{event_id}",
- path="status",
- role="status",
- ),
- ]
- event = resolver.event_details.get(int(event_id)) or {}
- if "duration_ms" in event:
- bindings.append(resolver.binding_for_event(
- event_id,
- binding_id=f"{prefix}:duration:event:{event_id}",
- path="duration_ms",
- role="status",
- ))
- if pointer_value(event, "/error_message") is not None or "error_message" in event:
- bindings.append(resolver.binding_for_event(
- event_id,
- binding_id=f"{prefix}:error:event:{event_id}",
- path="error_message",
- role="status",
- ))
- if include_output:
- outcome = resolver.binding_for_event(
- event_id,
- binding_id=f"{prefix}:outcome:event:{event_id}",
- path="output.content",
- role="output",
- availability="returned-to-agent",
- )
- outcome["transform"] = {
- "kind": "parsed",
- "operation": "retrieval-outcome-v1",
- "outputKey": "outcome",
- }
- outcome["evidence"]["confidence"] = "deterministic"
- bindings.append(outcome)
- completeness = resolver.binding_for_event(
- event_id,
- binding_id=f"{prefix}:completeness:event:{event_id}",
- path="",
- role="status",
- )
- completeness["transform"] = {
- "kind": "parsed",
- "operation": "event-body-completeness-v1",
- "outputKey": "completeness",
- }
- completeness["evidence"]["confidence"] = "deterministic"
- bindings.append(completeness)
- return bindings
- def _business_modules(detail: dict[str, Any]) -> list[dict[str, Any]]:
- if isinstance(detail.get("blocks"), list):
- modules: list[dict[str, Any]] = []
- for index, item in enumerate(detail.get("blocks") or [], 1):
- if not isinstance(item, dict):
- continue
- # Preserve the complete semantic block. Specialized business
- # blocks carry their real payload in routes/steps/branches/rows,
- # not in the generic value/items pair.
- module = dict(item)
- module["id"] = str(item.get("id") or f"block-{index}")
- module["title"] = item.get("title") or f"业务模块 {index}"
- if not module.get("items"):
- module["items"] = item.get("branches") or item.get("rows")
- modules.append(module)
- return modules
- return [
- dict(item)
- for item in detail.get("businessSections") or []
- if isinstance(item, dict)
- ]
- def _match_business_module(
- modules: list[dict[str, Any]], module_id: str, title: str
- ) -> dict[str, Any] | None:
- exact = next((item for item in modules if str(item.get("id")) == module_id), None)
- if exact:
- return exact
- normalized = _normalized_title(title)
- exact_title = next(
- (item for item in modules if _normalized_title(str(item.get("title") or "")) == normalized),
- None,
- )
- if exact_title:
- return exact_title
- # Do not use substring similarity here. A nearby title is not proof that
- # two independently shaped modules contain the same business value.
- return None
- def _module_value(module: dict[str, Any] | None) -> Any:
- if not module:
- return None
- if module.get("content") is not None:
- return module.get("content")
- if module.get("value") is not None:
- return module.get("value")
- block_type = str(module.get("type") or "")
- if block_type == "implementation-plan":
- return {
- "mode": module.get("mode"),
- "routes": module.get("routes") or [],
- "reasoning": module.get("reasoning"),
- }
- if block_type == "creative-process":
- return module.get("steps") or []
- if block_type == "evaluation-branches":
- return module.get("branches") or []
- if module.get("rows") is not None:
- return module.get("rows")
- if module.get("routes") is not None:
- return module.get("routes")
- if module.get("steps") is not None:
- return module.get("steps")
- return None
- def _presentation(module: dict[str, Any] | None, title: str) -> str:
- raw = str((module or {}).get("presentation") or (module or {}).get("type") or "")
- if raw in {"items", "list"}:
- return "items"
- if any(token in title for token in ("规划", "路线", "计划")):
- return "plan"
- if any(token in title for token in ("过程", "处理", "调用")):
- return "process"
- if "评审" in title:
- return "review"
- if any(token in title for token in ("候选表", "主脚本", "产物")):
- return "artifact"
- if any(token in title for token in ("说明", "缺失", "状态", "完整性")):
- return "notice"
- return "text"
- def _runtime_refs(bindings: list[SourceBinding]) -> list[str]:
- refs: list[str] = []
- for binding in bindings:
- source_id = str(binding.get("sourceId") or "")
- if source_id.startswith("event:"):
- refs.append(source_id)
- selector = binding.get("selector") or {}
- if selector.get("kind") == "members":
- refs.extend(
- str(member.get("sourceId") or "")
- for member in selector.get("members") or []
- if str(member.get("sourceId") or "").startswith("event:")
- )
- return list(dict.fromkeys(refs))
- def _historical_implementation_task_modules(
- fields: dict[str, dict[str, Any]],
- resolver: SourceResolver,
- ) -> list[dict[str, Any]]:
- """Keep old Branch tasks readable without copying one blob into six modules."""
- task_field = next(
- (field for field in fields.values() if str(field.get("label") or "") == "完整实现任务"),
- None,
- )
- target_field = next(
- (field for field in fields.values() if str(field.get("label") or "") == "实现目标"),
- None,
- )
- modules: list[dict[str, Any]] = []
- if target_field and target_field.get("value") not in (None, ""):
- binding = resolver.binding_for_field(target_field, binding_id="scope:historical-target")
- modules.append({
- "id": "scope",
- "title": "实现范围",
- "presentation": "text",
- "business": {"value": target_field.get("value")},
- "bindings": [binding],
- "runtimeRefs": [],
- "defaultOpen": True,
- })
- if task_field and task_field.get("value") not in (None, ""):
- binding = resolver.binding_for_field(task_field, binding_id="task:historical-full")
- modules.append({
- "id": "historical-task",
- "title": "完整实现任务(历史回退)",
- "presentation": "text",
- "business": {"value": task_field.get("value")},
- "bindings": [binding],
- "runtimeRefs": [],
- "defaultOpen": True,
- })
- if modules:
- return modules
- binding = unresolved_binding(
- "historical-task:missing",
- reason="该历史方案没有保存实现 Agent 输入,也没有可用的 Branch 实现任务字段",
- code="source-record-not-saved",
- )
- resolver.add_unresolved_source(binding)
- return [{
- "id": "historical-task",
- "title": "实现任务记录缺失",
- "presentation": "notice",
- "business": {"value": "该历史方案未保存可恢复的完整实现任务。"},
- "bindings": [binding],
- "runtimeRefs": [],
- "defaultOpen": True,
- }]
- def _multipath_comparison_binding(
- item: dict[str, Any],
- item_index: int,
- module_bindings: list[SourceBinding],
- resolver: SourceResolver,
- bundle: dict[str, Any],
- ) -> SourceBinding:
- """Bind one comparison row to its review, Branch status and final decision."""
- text = str(item.get("value") or item.get("label") or "")
- match = re.search(r"方案\s*(\d+)", text)
- branch_id = int(match.group(1)) if match else None
- decision_id = next(
- (
- _suffix_int(str(binding.get("sourceId") or ""))
- for binding in module_bindings
- if "script_build_multipath_decision" in str(binding.get("sourceId") or "")
- ),
- None,
- )
- members: list[dict[str, Any]] = []
- if branch_id is not None:
- branch = next(
- (row for row in bundle.get("branches") or [] if _integer(row.get("branch_id")) == branch_id),
- None,
- )
- if branch is not None:
- members.append({
- "id": f"comparison:{item_index}:branch",
- "label": f"方案 {branch_id} 处置状态",
- "value": branch.get("status"),
- "source": {
- "kind": "database",
- "label": "script_build_branch",
- "ref": f"round:{branch.get('round_index')}:branch:{branch_id}:output",
- "fieldPath": "status",
- },
- "relation": "persisted-output",
- })
- if decision_id is not None:
- decision = next(
- (row for row in bundle.get("multipathDecisions") or [] if _integer(row.get("id")) == decision_id),
- None,
- )
- if decision is not None:
- members.append({
- "id": f"comparison:{item_index}:decision",
- "label": "主 Agent 最终决定",
- "value": decision.get("decision"),
- "source": {
- "kind": "database",
- "label": "script_build_multipath_decision",
- "ref": f"multipath-decision:{decision_id}",
- "fieldPath": "decision",
- },
- "relation": "persisted-output",
- })
- for binding in module_bindings:
- source_id = str(binding.get("sourceId") or "")
- event_id = _suffix_int(source_id) if source_id.startswith("event:") else None
- if event_id is None:
- continue
- event = resolver.event_details.get(event_id)
- if event is None:
- continue
- members.append({
- "id": f"comparison:{item_index}:review:{event_id}",
- "label": "多方案评审",
- "value": (event.get("output") or {}).get("content") if isinstance(event.get("output"), dict) else event.get("output"),
- "source": {
- "kind": "runtime-event",
- "label": str(event.get("event_name") or f"Event {event_id}"),
- "ref": f"event:{event_id}",
- "fieldPath": "output.content",
- },
- "relation": "available-upstream",
- })
- return resolver.members_binding(
- {
- "id": f"comparison:item:{item_index}",
- "label": str(item.get("label") or f"方案对照 {item_index}"),
- "value": item.get("value"),
- },
- binding_id=f"comparison:item:{item_index}",
- members=members,
- reducer="multipath-review-and-decision-row-v1",
- role="basis",
- )
- def _align_transforms_with_business(
- value: Any,
- bindings: list[SourceBinding],
- resolver: SourceResolver,
- module_id: str,
- ) -> None:
- """Make the transform describe what the first column actually renders."""
- if value is None or not bindings:
- return
- if len(bindings) > 1:
- for binding in bindings:
- if binding.get("transform", {}).get("kind") == "direct":
- binding["transform"] = {
- "kind": "combined",
- "operation": f"business-module:{module_id}",
- }
- binding["evidence"]["confidence"] = "deterministic"
- return
- binding = bindings[0]
- if binding.get("transform", {}).get("kind") != "direct":
- return
- source = resolver.sources.get(str(binding.get("sourceId") or "")) or {}
- selector = binding.get("selector") or {}
- if selector.get("kind") == "json-pointer":
- selected = pointer_value(source.get("rawRecord"), str(selector.get("path") or ""))
- elif selector.get("kind") == "whole-record":
- selected = source.get("rawRecord")
- else:
- return
- if not _same_value(value, selected):
- binding["transform"] = {
- "kind": "parsed",
- "operation": "business-module-projection-v1",
- "outputKey": module_id,
- }
- binding["evidence"]["confidence"] = "deterministic"
- def _normalize_items(items: list[Any]) -> list[dict[str, Any]]:
- values: list[dict[str, Any]] = []
- for index, item in enumerate(items, 1):
- if isinstance(item, dict):
- label = (
- item.get("label")
- or item.get("title")
- or item.get("subject")
- or item.get("summary")
- or f"第 {index} 项"
- )
- value = item.get("value") if "value" in item else item
- values.append({"label": label, "value": value})
- else:
- values.append({"label": f"第 {index} 项", "value": item})
- return values
- def _item_bindings(
- module_id: str,
- item_index: int,
- item: dict[str, Any],
- item_count: int,
- module_bindings: list[SourceBinding],
- resolver: SourceResolver,
- ) -> list[SourceBinding]:
- if not module_bindings:
- binding = unresolved_binding(
- f"{module_id}:item:{item_index}:unresolved",
- reason=f"子项「{item.get('label')}」没有可定位的来源绑定",
- code="binding-map-missing",
- )
- resolver.add_unresolved_source(binding)
- return [binding]
- if len(module_bindings) == item_count and item_index <= len(module_bindings):
- binding = deepcopy(module_bindings[item_index - 1])
- binding["id"] = f"{module_id}:item:{item_index}"
- source = resolver.sources.get(str(binding.get("sourceId") or "")) or {}
- selector = binding.get("selector") or {}
- selected = pointer_value(source.get("rawRecord"), str(selector.get("path") or ""))
- if not _same_value(item.get("value"), selected):
- binding["transform"] = {
- "kind": "parsed",
- "operation": "business-item-v1",
- "outputKey": str(item_index),
- }
- binding["evidence"]["confidence"] = "deterministic"
- return [binding]
- parent = module_bindings[0]
- selector = parent.get("selector") or {}
- source_id = str(parent.get("sourceId") or "")
- source_record = resolver.sources.get(source_id) or {}
- selected_parent = pointer_value(
- source_record.get("rawRecord"), str(selector.get("path") or "")
- )
- if selector.get("kind") == "json-pointer" and isinstance(selected_parent, list):
- binding = deepcopy(parent)
- binding["id"] = f"{module_id}:item:{item_index}"
- binding["selector"] = {
- "kind": "json-pointer",
- "path": f"{str(selector.get('path') or '').rstrip('/')}/{item_index - 1}",
- }
- return [binding]
- event_id = _suffix_int(source_id) if source_id.startswith("event:") else None
- if event_id:
- role = str(parent.get("role") or "basis")
- path = str(selector.get("path") or "")
- if not path:
- path = "input.content.task" if role in {"input", "constraint", "basis"} else "output.content.summary"
- return [
- resolver.text_span_binding(
- event_id,
- binding_id=f"{module_id}:item:{item_index}",
- path=path.lstrip("/").replace("/", "."),
- exact_text=_item_text(item.get("value")),
- role=role,
- )
- ]
- binding = unresolved_binding(
- f"{module_id}:item:{item_index}:unresolved",
- reason=f"子项「{item.get('label')}」是结构化重组结果,无法安全定位到单一原始区间",
- source_id=source_id or None,
- resolution="unsafe",
- code="unsafe-association",
- )
- if source_id and source_id in resolver.sources:
- return [binding]
- resolver.add_unresolved_source(binding)
- return [binding]
- def _item_text(value: Any) -> str:
- if isinstance(value, str):
- return value
- if not isinstance(value, dict):
- return str(value or "")
- for key in ("summary", "conclusion", "subject", "title", "evidence"):
- if value.get(key):
- return str(value[key])
- return ""
- def _leaf_texts(value: Any) -> list[str]:
- """Return every visible textual leaf so a structured review item is fully bound."""
- result: list[str] = []
- if isinstance(value, str):
- cleaned = value.strip()
- if cleaned:
- result.append(cleaned)
- elif isinstance(value, dict):
- for child in value.values():
- result.extend(_leaf_texts(child))
- elif isinstance(value, list):
- for child in value:
- result.extend(_leaf_texts(child))
- elif value is not None:
- result.append(str(value))
- return list(dict.fromkeys(result))
- def _same_value(left: Any, right: Any) -> bool:
- try:
- import json
- return json.dumps(left, ensure_ascii=False, sort_keys=True, default=str) == json.dumps(
- right, ensure_ascii=False, sort_keys=True, default=str
- )
- except (TypeError, ValueError):
- return str(left) == str(right)
- def _unique_bindings(bindings: list[SourceBinding]) -> list[SourceBinding]:
- result: list[SourceBinding] = []
- seen: set[str] = set()
- for binding in bindings:
- key = str(binding.get("id") or "")
- if key in seen:
- continue
- seen.add(key)
- result.append(binding)
- return result
- def _all_bindings(modules: list[dict[str, Any]]) -> list[SourceBinding]:
- result: list[SourceBinding] = []
- for module in modules:
- result.extend(module.get("bindings") or [])
- business = module.get("business") or {}
- for item in business.get("items") or []:
- if isinstance(item, dict):
- result.extend(item.get("bindings") or [])
- return result
- def _used_source_ids(modules: list[dict[str, Any]]) -> set[str]:
- used: set[str] = set()
- def add_selector_members(selector: dict[str, Any]) -> None:
- if selector.get("kind") != "members":
- return
- for member in selector.get("members") or []:
- source_id = str(member.get("sourceId") or "")
- if source_id:
- used.add(source_id)
- child = member.get("selector")
- if isinstance(child, dict):
- add_selector_members(child)
- for module in modules:
- for binding in module.get("bindings") or []:
- source_id = str(binding.get("sourceId") or "")
- if source_id:
- used.add(source_id)
- selector = binding.get("selector")
- if isinstance(selector, dict):
- add_selector_members(selector)
- for source_id in module.get("runtimeRefs") or []:
- if isinstance(source_id, str) and source_id:
- used.add(source_id)
- return used
- def _aggregate_binding(
- card_kind: str,
- module_id: str,
- source_ids: list[str],
- fields: dict[str, dict[str, Any]],
- resolver: SourceResolver,
- bundle: dict[str, Any],
- ) -> SourceBinding | None:
- if not source_ids:
- return None
- target = next((fields[source_id] for source_id in source_ids if source_id in fields), None)
- if target is None:
- return None
- eligible = (
- (card_kind == "round-result" and module_id in {"completion", "dispositions", "domain", "changes"})
- or (card_kind == "round-summary" and module_id == "plan")
- or (card_kind == "final-result" and module_id in {"stats", "scale"})
- or (card_kind == "domain-facts" and module_id in {"facts", "verification", "sources", "usage"})
- or (card_kind == "retrieval-stage" and module_id in {"mode", "waves", "direct", "agents", "result"})
- )
- if not eligible:
- return None
- members: list[dict[str, Any]] = []
- reducer = str((target.get("source") or {}).get("label") or "确定性聚合")
- if card_kind == "round-summary" and module_id == "plan":
- round_index = _round_index_from_field_id(str(target.get("id") or ""))
- row = next(
- (
- item
- for item in bundle.get("rounds") or []
- if _integer(item.get("round_index")) == round_index
- ),
- None,
- )
- if row:
- for path in ("race_or_divide", "multipath_plan", "plan_note"):
- members.append(
- _member_field(
- "script_build_round",
- f"round:{round_index}",
- row,
- path,
- )
- )
- elif card_kind == "retrieval-stage":
- value = target.get("value")
- event_ids: list[int] = []
- def collect_event_ids(node: Any) -> None:
- if isinstance(node, dict):
- event_id = _integer(node.get("eventId"))
- if event_id:
- event_ids.append(event_id)
- for child in node.values():
- collect_event_ids(child)
- elif isinstance(node, list):
- for child in node:
- collect_event_ids(child)
- collect_event_ids(value)
- # Mode/wave/result are deterministic summaries of the whole scoped
- # retrieval stage, so their members are every Event loaded for this
- # card. Direct/Agent modules only retain the Events named by their
- # own structured projection.
- if module_id in {"mode", "waves", "result"}:
- event_ids.extend(resolver.event_details)
- for event_id in dict.fromkeys(event_ids):
- event = resolver.event_details.get(event_id)
- if not event:
- continue
- members.append(
- {
- "id": f"aggregate:retrieval:{module_id}:event:{event_id}",
- "label": str(event.get("event_name") or f"Event {event_id}"),
- "value": event,
- "source": {
- "kind": "runtime-event",
- "label": str(event.get("event_name") or f"Event {event_id}"),
- "ref": f"event:{event_id}",
- "fieldPath": "",
- },
- "relation": "persisted-output",
- "completeness": "complete",
- }
- )
- elif card_kind == "round-result":
- round_index = _round_index_from_field_id(str(target.get("id") or ""))
- if module_id in {"dispositions", "changes", "completion"}:
- for row in bundle.get("branches") or []:
- if _integer(row.get("round_index")) != round_index:
- continue
- branch_id = _integer(row.get("branch_id"))
- path = "status" if module_id == "dispositions" else "candidate_snapshot"
- members.append(
- {
- "id": f"aggregate:{module_id}:branch:{branch_id}",
- "label": f"方案 {branch_id}",
- "value": row.get(path),
- "source": {
- "kind": "database",
- "label": "script_build_branch",
- "ref": f"round:{round_index}:branch:{branch_id}:output",
- "fieldPath": path,
- },
- "relation": "persisted-output",
- "completeness": "complete",
- }
- )
- elif module_id == "domain":
- for row in bundle.get("domainInfo") or []:
- if _integer(row.get("round_index")) != round_index:
- continue
- members.append(
- {
- "id": f"aggregate:domain:{row.get('id')}",
- "label": "领域事实",
- "value": row,
- "source": {
- "kind": "database",
- "label": "script_build_domain_info",
- "ref": f"domain-info:{row.get('id')}",
- "fieldPath": "",
- },
- "relation": "persisted-output",
- "completeness": "complete",
- }
- )
- elif card_kind == "final-result" and module_id == "stats":
- for row in bundle.get("rounds") or []:
- members.append(_member_field("script_build_round", f"round:{row.get('round_index')}:result", row, "status"))
- for row in bundle.get("branches") or []:
- members.append(_member_field("script_build_branch", f"round:{row.get('round_index')}:branch:{row.get('branch_id')}:output", row, "status"))
- elif card_kind == "final-result" and module_id == "scale":
- artifact = bundle.get("currentArtifact") or {}
- for collection in ("paragraphs", "elements", "paragraphElements"):
- members.append(
- {
- "id": f"aggregate:artifact:{collection}",
- "label": collection,
- "value": artifact.get(collection) or [],
- "source": {
- "kind": "artifact",
- "label": "当前主脚本",
- "ref": "artifact:base:current",
- "fieldPath": collection,
- },
- "relation": "persisted-output",
- "completeness": "complete",
- }
- )
- elif card_kind == "domain-facts":
- match = re.search(r"branch:(\d+)", str(target.get("id") or ""))
- branch_id = _integer(match.group(1)) if match else None
- path = {
- "facts": "content",
- "verification": "note",
- "sources": "source",
- "usage": "content",
- }.get(module_id, "content")
- for row in bundle.get("domainInfo") or []:
- if _integer(row.get("branch_id")) != branch_id:
- continue
- members.append(
- {
- "id": f"aggregate:domain:{row.get('id')}:{path}",
- "label": f"领域事实 {row.get('id')}",
- "value": row.get(path),
- "source": {
- "kind": "database",
- "label": "script_build_domain_info",
- "ref": f"domain-info:{row.get('id')}",
- "fieldPath": path,
- },
- "relation": "persisted-output",
- "completeness": "complete" if row.get(path) not in (None, "", [], {}) else "partial",
- }
- )
- if not members and card_kind == "domain-facts":
- match = re.search(r"branch:(\d+)", str(target.get("id") or ""))
- branch_id = _integer(match.group(1)) if match else None
- return resolver.empty_database_result_binding(
- binding_id=f"{module_id}:empty-result",
- table="script_build_domain_info",
- filters={"branch_id": branch_id},
- label="领域事实查询结果",
- )
- if not members:
- return None
- return resolver.members_binding(
- target,
- binding_id=f"{module_id}:aggregate",
- members=members,
- reducer=reducer,
- )
- def _member_field(table: str, ref: str, row: dict[str, Any], path: str) -> dict[str, Any]:
- return {
- "id": f"aggregate:{table}:{row.get('id')}:{path}",
- "label": f"{table}.{path}",
- "value": row.get(path),
- "source": {
- "kind": "database",
- "label": table,
- "ref": ref,
- "fieldPath": path,
- },
- "relation": "persisted-output",
- "completeness": "complete",
- }
- def _round_index_from_field_id(value: str) -> int | None:
- match = re.search(r"round:(\d+)", value)
- return _integer(match.group(1)) if match else None
- def _business_notices(detail: dict[str, Any]) -> list[dict[str, Any]]:
- notices: list[dict[str, Any]] = []
- for item in detail.get("notices") or []:
- if isinstance(item, dict) and item.get("message"):
- notices.append(item)
- if detail.get("missingActivityNotice"):
- notices.append({"level": "warning", "message": detail["missingActivityNotice"]})
- return notices
- def _unique_notices(items: list[dict[str, Any]]) -> list[dict[str, Any]]:
- result: list[dict[str, Any]] = []
- seen: set[str] = set()
- for item in items:
- message = str(item.get("message") or "")
- if not message or message in seen:
- continue
- seen.add(message)
- result.append({"level": item.get("level") or "info", "message": message})
- return result
- def _fallback_card_data(detail_ref: str, business: dict[str, Any]) -> dict[str, Any]:
- modules = _business_modules(business)
- return {
- "cardKind": _fallback_card_kind(detail_ref),
- "completeness": "partial",
- "businessInputs": [],
- "businessOutputs": [],
- "runtime": {"summary": "", "units": []},
- "displayUse": {
- "businessModules": [
- {"id": item.get("id") or f"module-{index}", "title": item.get("title") or f"业务模块 {index}", "sourceIds": []}
- for index, item in enumerate(modules, 1)
- ],
- "cardFields": [],
- "unusedSourceIds": [],
- },
- "notices": [
- {
- "level": "warning",
- "message": "该历史卡片没有完整结构化来源投影,已保留业务详情并显式标记断链。",
- }
- ],
- }
- def _fallback_card_kind(detail_ref: str) -> str:
- if detail_ref.startswith("event:"):
- return "runtime-event"
- if detail_ref.startswith("round:"):
- return detail_ref.rsplit(":", 1)[-1]
- return detail_ref.split(":", 1)[0] or "unknown"
- def _event_text_path(event: dict[str, Any], side: str) -> str:
- wrapped = event.get(side)
- content = wrapped.get("content") if isinstance(wrapped, dict) else None
- if isinstance(content, dict):
- preferred = "task" if side == "input" else "summary"
- if isinstance(content.get(preferred), str):
- return f"{side}.content.{preferred}"
- return f"{side}.content"
- def _normalized_title(value: str) -> str:
- return re.sub(r"[\s/\u3001,,()()]+", "", value).replace("完整", "")
- def _collect_event_ids(value: Any, ids: set[int], key: str = "") -> None:
- if isinstance(value, dict):
- for child_key, child in value.items():
- lowered = str(child_key).lower()
- if lowered in {
- "eventid",
- "event_id",
- "implementereventid",
- "scopeeventid",
- "parenteventid",
- }:
- event_id = _integer(child)
- if event_id:
- ids.add(event_id)
- elif lowered in {"eventref", "currentrevisionref", "detailref"}:
- event_id = _suffix_int(str(child))
- if event_id:
- ids.add(event_id)
- elif lowered in {"eventrefs", "technicalrefs", "revisionrefs"}:
- for ref in child or []:
- event_id = _suffix_int(str(ref))
- if event_id:
- ids.add(event_id)
- _collect_event_ids(child, ids, lowered)
- elif isinstance(value, list):
- for child in value:
- _collect_event_ids(child, ids, key)
- def _view_branch(view: dict[str, Any], round_index: int, branch_id: int) -> dict[str, Any]:
- round_ = next(
- (item for item in view.get("rounds") or [] if _integer(item.get("roundIndex")) == round_index),
- {},
- )
- return next(
- (item for item in round_.get("branches") or [] if _integer(item.get("branchId")) == branch_id),
- {},
- )
- def _bundle_event(bundle: dict[str, Any], event_id: int | None) -> dict[str, Any] | None:
- return next(
- (item for item in bundle.get("events") or [] if _integer(item.get("id")) == event_id),
- None,
- )
- def _integer(value: Any) -> int | None:
- try:
- return int(value)
- except (TypeError, ValueError):
- return None
- def _suffix_int(value: str) -> int | None:
- return _integer(str(value or "").rsplit(":", 1)[-1])
|