@@ -27,10 +27,23 @@ type Component = Record<string, unknown> & {
2727 component : string ;
2828} ;
2929
30+ /**
31+ * Record of a single data-fetch step the compiler ran while assembling
32+ * the dashboard. The route surfaces these as synthetic AG-UI tool-call
33+ * events so the user keeps the same "tool calls" visibility they had
34+ * before the DSL refactor (where every call originated from the LLM).
35+ */
36+ export interface DataStep {
37+ name : string ;
38+ args : unknown ;
39+ result ?: unknown ;
40+ }
41+
3042export interface CompiledDashboard {
3143 surfaceId : string ;
3244 structural : A2uiMessage [ ] ;
3345 dataModel : A2uiMessage [ ] ;
46+ dataSteps : DataStep [ ] ;
3447}
3548
3649interface DashboardData {
@@ -56,12 +69,14 @@ export async function compileDashboard(
5669 spec : DashboardSpec ,
5770 options : { surfaceId ?: string } = { } ,
5871) : Promise < CompiledDashboard > {
59- const data = await fetchAllDashboardData ( spec ) ;
60- return assembleDashboard ( spec , data , options . surfaceId ) ;
72+ const dataSteps : DataStep [ ] = [ ] ;
73+ const data = await fetchAllDashboardData ( spec , dataSteps ) ;
74+ return assembleDashboard ( spec , data , options . surfaceId , dataSteps ) ;
6175}
6276
6377async function fetchAllDashboardData (
6478 spec : DashboardSpec ,
79+ dataSteps : DataStep [ ] ,
6580) : Promise < DashboardData > {
6681 const routes = new Set < string > ( ) ;
6782 let needsBookedFlights = false ;
@@ -88,6 +103,19 @@ async function fetchAllDashboardData(
88103 }
89104
90105 const routeList = [ ...routes ] ;
106+ const flightStepIndices : number [ ] = [ ] ;
107+ for ( const key of routeList ) {
108+ const [ from , to ] = key . split ( '|' ) ;
109+ flightStepIndices . push (
110+ dataSteps . push ( { name : 'searchFlights' , args : { from, to } } ) - 1 ,
111+ ) ;
112+ }
113+ let findBookedStepIdx : number | null = null ;
114+ if ( needsBookedFlights ) {
115+ findBookedStepIdx =
116+ dataSteps . push ( { name : 'findBookedFlights' , args : { } } ) - 1 ;
117+ }
118+
91119 const [ bookedFlights , ...flightLists ] = await Promise . all ( [
92120 needsBookedFlights ? getBookedFlights ( ) : Promise . resolve ( [ ] ) ,
93121 ...routeList . map ( ( key ) => {
@@ -96,6 +124,13 @@ async function fetchAllDashboardData(
96124 } ) ,
97125 ] ) ;
98126
127+ flightStepIndices . forEach ( ( stepIdx , i ) => {
128+ dataSteps [ stepIdx ] . result = { count : flightLists [ i ] ?. length ?? 0 } ;
129+ } ) ;
130+ if ( findBookedStepIdx !== null ) {
131+ dataSteps [ findBookedStepIdx ] . result = { count : bookedFlights . length } ;
132+ }
133+
99134 const flightsByRoute = new Map < string , FlightRecord [ ] > ( ) ;
100135 routeList . forEach ( ( key , idx ) => {
101136 flightsByRoute . set ( key , flightLists [ idx ] ?? [ ] ) ;
@@ -108,6 +143,7 @@ function assembleDashboard(
108143 spec : DashboardSpec ,
109144 data : DashboardData ,
110145 givenSurfaceId : string | undefined ,
146+ dataSteps : DataStep [ ] ,
111147) : CompiledDashboard {
112148 const surfaceId = givenSurfaceId ?? `dash-${ randomUUID ( ) } ` ;
113149
@@ -116,7 +152,7 @@ function assembleDashboard(
116152 const rootChildren : string [ ] = [ ] ;
117153
118154 spec . tiles . forEach ( ( tile , idx ) => {
119- const result = buildTile ( idx , tile , data , surfaceId ) ;
155+ const result = buildTile ( idx , tile , data , surfaceId , dataSteps ) ;
120156 rootChildren . push ( ...result . rootChildren ) ;
121157 allComponents . push ( ...result . components ) ;
122158 allDataOps . push ( ...result . dataOps ) ;
@@ -140,36 +176,37 @@ function assembleDashboard(
140176 } as unknown as A2uiMessage ,
141177 ] ;
142178
143- return { surfaceId, structural, dataModel : allDataOps } ;
179+ return { surfaceId, structural, dataModel : allDataOps , dataSteps } ;
144180}
145181
146182function buildTile (
147183 idx : number ,
148184 tile : DashboardTile ,
149185 data : DashboardData ,
150186 surfaceId : string ,
187+ dataSteps : DataStep [ ] ,
151188) : TileBuildResult {
152189 switch ( tile . type ) {
153190 case 'flightsTable' :
154191 return buildFlightsTable ( idx , tile , data , surfaceId , false ) ;
155192 case 'delayedFlightsTable' :
156193 return buildFlightsTable ( idx , tile , data , surfaceId , true ) ;
157194 case 'delayShareChart' :
158- return buildDelayShareChart ( idx , tile , data , surfaceId ) ;
195+ return buildDelayShareChart ( idx , tile , data , surfaceId , dataSteps ) ;
159196 case 'delaysPerDayChart' :
160- return buildDelaysPerDayChart ( idx , tile , data , surfaceId ) ;
197+ return buildDelaysPerDayChart ( idx , tile , data , surfaceId , dataSteps ) ;
161198 case 'boardingPasses' :
162199 return buildBoardingPasses ( idx , tile , data , surfaceId ) ;
163200 case 'bookedFlightsList' :
164- return buildBookedFlightsList ( idx , data , surfaceId ) ;
201+ return buildBookedFlightsList ( idx , data , surfaceId , dataSteps ) ;
165202 case 'flightSearch' :
166203 return buildFlightSearch ( idx , tile , surfaceId ) ;
167204 case 'rentalCars' :
168- return buildRentalCars ( idx , tile , data , surfaceId ) ;
205+ return buildRentalCars ( idx , tile , data , surfaceId , dataSteps ) ;
169206 case 'hotels' :
170- return buildHotels ( idx , tile , data , surfaceId ) ;
207+ return buildHotels ( idx , tile , data , surfaceId , dataSteps ) ;
171208 case 'weatherList' :
172- return buildWeatherList ( idx , data , surfaceId ) ;
209+ return buildWeatherList ( idx , data , surfaceId , dataSteps ) ;
173210 }
174211}
175212
@@ -294,6 +331,7 @@ function buildDelayShareChart(
294331 tile : Extract < DashboardTile , { type : 'delayShareChart' } > ,
295332 data : DashboardData ,
296333 surfaceId : string ,
334+ dataSteps : DataStep [ ] ,
297335) : TileBuildResult {
298336 const flights = data . flightsByRoute . get ( routeKey ( tile . from , tile . to ) ) ?? [ ] ;
299337 let onTime = 0 ;
@@ -302,12 +340,23 @@ function buildDelayShareChart(
302340 if ( f . delay > 0 ) delayed += 1 ;
303341 else onTime += 1 ;
304342 }
343+ const chartType = tile . chartType ?? 'pie' ;
305344 const url = buildAndCacheChartUrl ( {
306- type : tile . chartType ?? 'pie' ,
345+ type : chartType ,
307346 title : `On-time vs. delayed (${ tile . from } → ${ tile . to } )` ,
308347 labels : [ 'On time' , 'Delayed' ] ,
309348 datasets : [ { label : 'Flights' , data : [ onTime , delayed ] } ] ,
310349 } ) ;
350+ dataSteps . push ( {
351+ name : 'renderFlightChart' ,
352+ args : {
353+ from : tile . from ,
354+ to : tile . to ,
355+ type : 'delayShare' ,
356+ chartType,
357+ } ,
358+ result : { onTime, delayed, total : onTime + delayed , url } ,
359+ } ) ;
311360 return chartTile (
312361 idx ,
313362 surfaceId ,
@@ -321,6 +370,7 @@ function buildDelaysPerDayChart(
321370 tile : Extract < DashboardTile , { type : 'delaysPerDayChart' } > ,
322371 data : DashboardData ,
323372 surfaceId : string ,
373+ dataSteps : DataStep [ ] ,
324374) : TileBuildResult {
325375 const flights = data . flightsByRoute . get ( routeKey ( tile . from , tile . to ) ) ?? [ ] ;
326376 const buckets = new Map < string , { onTime : number ; delayed : number } > ( ) ;
@@ -343,6 +393,16 @@ function buildDelaysPerDayChart(
343393 { label : 'Delayed' , data : sortedDays . map ( ( [ , v ] ) => v . delayed ) } ,
344394 ] ,
345395 } ) ;
396+ dataSteps . push ( {
397+ name : 'renderFlightChart' ,
398+ args : {
399+ from : tile . from ,
400+ to : tile . to ,
401+ type : 'delaysPerDay' ,
402+ chartType : 'bar' ,
403+ } ,
404+ result : { days : sortedDays . length , url } ,
405+ } ) ;
346406 return chartTile (
347407 idx ,
348408 surfaceId ,
@@ -426,6 +486,7 @@ function buildBookedFlightsList(
426486 idx : number ,
427487 data : DashboardData ,
428488 surfaceId : string ,
489+ dataSteps : DataStep [ ] ,
429490) : TileBuildResult {
430491 const flights = data . bookedFlights ;
431492 const cardId = tileId ( idx , 'card' ) ;
@@ -511,6 +572,11 @@ function buildBookedFlightsList(
511572 ) ;
512573
513574 const w = weatherForecast ( flight . to , flight . date ) ;
575+ dataSteps . push ( {
576+ name : 'weatherForecast' ,
577+ args : { city : flight . to , date : flight . date . slice ( 0 , 10 ) } ,
578+ result : { condition : w . condition , temperatureC : w . temperatureC } ,
579+ } ) ;
514580 const meta = `${ flight . date . slice ( 0 , 10 ) } · ${ weatherIconFor ( w . condition ) } ${ w . condition } — ${ w . temperatureC } °C · ${
515581 flight . delay > 0 ? `Delayed by ${ flight . delay } min` : 'On time'
516582 } `;
@@ -610,9 +676,15 @@ function buildRentalCars(
610676 tile : Extract < DashboardTile , { type : 'rentalCars' } > ,
611677 data : DashboardData ,
612678 surfaceId : string ,
679+ dataSteps : DataStep [ ] ,
613680) : TileBuildResult {
614681 const city = tile . city ?? data . bookedFlights [ 0 ] ?. to ?? FALLBACK_CITY ;
615682 const result = searchRentalCars ( city ) ;
683+ dataSteps . push ( {
684+ name : 'searchRentalCars' ,
685+ args : { city } ,
686+ result : { count : result . cars . length } ,
687+ } ) ;
616688 return imageRowList ( {
617689 idx,
618690 surfaceId,
@@ -630,9 +702,15 @@ function buildHotels(
630702 tile : Extract < DashboardTile , { type : 'hotels' } > ,
631703 data : DashboardData ,
632704 surfaceId : string ,
705+ dataSteps : DataStep [ ] ,
633706) : TileBuildResult {
634707 const city = tile . city ?? data . bookedFlights [ 0 ] ?. to ?? FALLBACK_CITY ;
635708 const result = searchHotels ( city ) ;
709+ dataSteps . push ( {
710+ name : 'searchHotels' ,
711+ args : { city } ,
712+ result : { count : result . hotels . length } ,
713+ } ) ;
636714 return imageRowList ( {
637715 idx,
638716 surfaceId,
@@ -649,6 +727,7 @@ function buildWeatherList(
649727 idx : number ,
650728 data : DashboardData ,
651729 surfaceId : string ,
730+ dataSteps : DataStep [ ] ,
652731) : TileBuildResult {
653732 const flights = data . bookedFlights ;
654733 const cardId = tileId ( idx , 'card' ) ;
@@ -694,6 +773,11 @@ function buildWeatherList(
694773 variant : 'body' ,
695774 } ) ;
696775 const w = weatherForecast ( flight . to , flight . date ) ;
776+ dataSteps . push ( {
777+ name : 'weatherForecast' ,
778+ args : { city : flight . to , date : flight . date . slice ( 0 , 10 ) } ,
779+ result : { condition : w . condition , temperatureC : w . temperatureC } ,
780+ } ) ;
697781 const line = `${ flight . to } · ${ flight . date . slice ( 0 , 10 ) } · ${ weatherIconFor ( w . condition ) } ${ w . condition } — ${ w . temperatureC } °C` ;
698782 dataOps . push ( dataOp ( surfaceId , path , line ) ) ;
699783 } ) ;
0 commit comments