66from typing import Any
77
88from .protocol import ExecutionEvent , ExecutionOutput , WsResultMessage
9- from .reactive import executeReactive , previewReactiveOrder , reactiveDiagnostics
9+ from .reactive import buildReactiveGraph , diagnosticsFromGraph , executeReactive , previewReactiveOrder
1010from .session import KernelSession
1111
1212
@@ -28,6 +28,10 @@ class KernelReactivePayload:
2828 executionOrder : tuple [str , ...]
2929 durationMs : float
3030 cycles : tuple [tuple [str , ...], ...] = ()
31+ multipleDefinitions : tuple [tuple [str , tuple [str , ...]], ...] = ()
32+ crossCellMutations : tuple [tuple [str , str , str ], ...] = ()
33+ staleBlockIds : tuple [str , ...] = ()
34+ dependents : tuple [tuple [str , tuple [str , ...]], ...] = ()
3135
3236 @property
3337 def resultCount (self ) -> int :
@@ -37,11 +41,20 @@ def resultCount(self) -> int:
3741 def executionCount (self ) -> int :
3842 return len (self .executionOrder )
3943
44+ def _diagnosticsPayload (self ) -> dict [str , Any ]:
45+ return {
46+ "cycles" : [list (cycle ) for cycle in self .cycles ],
47+ "multipleDefinitions" : [[var , list (blockIds )] for var , blockIds in self .multipleDefinitions ],
48+ "crossCellMutations" : [list (mutation ) for mutation in self .crossCellMutations ],
49+ "staleBlockIds" : list (self .staleBlockIds ),
50+ "dependents" : {blockId : list (downstream ) for blockId , downstream in self .dependents },
51+ }
52+
4053 def httpPayload (self ) -> dict [str , Any ]:
4154 return {
4255 "results" : [result .model_dump () for result in self .results ],
4356 "executionOrder" : list (self .executionOrder ),
44- "cycles" : [ list ( cycle ) for cycle in self .cycles ] ,
57+ ** self ._diagnosticsPayload () ,
4558 }
4659
4760 def toolPayload (self ) -> dict [str , Any ]:
@@ -67,7 +80,7 @@ def wsCompletePayload(self, requestId: str) -> dict[str, Any]:
6780 "type" : "reactiveComplete" ,
6881 "requestId" : requestId ,
6982 "executionOrder" : list (self .executionOrder ),
70- "cycles" : [ list ( cycle ) for cycle in self .cycles ] ,
83+ ** self ._diagnosticsPayload () ,
7184 }
7285
7386
@@ -93,15 +106,28 @@ async def executeKernelReactive(
93106) -> KernelReactivePayload :
94107 startedAt = time .perf_counter ()
95108 blockList = list (blocks )
96- cycles = reactiveDiagnostics (blockList )
109+ graph = buildReactiveGraph (blockList )
110+ diagnostics = diagnosticsFromGraph (graph )
97111 results , executionOrder = await executeReactive (
98- session , blockList , changedBlockId , eventHandler = eventHandler , includeSource = includeSource
112+ session , blockList , changedBlockId , eventHandler = eventHandler , includeSource = includeSource , graph = graph
113+ )
114+ # early-stop(에러로 중단)으로 영향받았지만 실행 못 한 셀 = stale.
115+ executed = {result .blockId for result in results }
116+ staleBlockIds = tuple (blockId for blockId in executionOrder if blockId not in executed )
117+ dependents = tuple (
118+ (blockId , tuple (sorted (graph .dependents [blockId ])))
119+ for blockId in graph .blockOrder
120+ if graph .dependents .get (blockId )
99121 )
100122 return KernelReactivePayload (
101123 results = tuple (results ),
102124 executionOrder = tuple (executionOrder ),
103125 durationMs = _durationMs (startedAt ),
104- cycles = tuple (tuple (cycle ) for cycle in cycles ),
126+ cycles = diagnostics .cycles ,
127+ multipleDefinitions = diagnostics .multipleDefinitions ,
128+ crossCellMutations = diagnostics .crossCellMutations ,
129+ staleBlockIds = staleBlockIds ,
130+ dependents = dependents ,
105131 )
106132
107133
0 commit comments