@@ -5,11 +5,14 @@ import type { BaseLlm } from 'adk-llm-bridge';
55import createDebug from 'debug' ;
66import { extractCodeBlock } from '../extract-code-block.js' ;
77import { extractExportedNames } from '../extract-exports.js' ;
8+ import { PipelineLogger } from '../pipeline-logger.js' ;
89import { generatePlaceholderComponent , generatePlaceholderStory } from '../placeholder.js' ;
910import { sanitizeBodyWithImports } from '../sanitize-body.js' ;
1011import { generateScaffold , type ScaffoldComposedComponent } from '../scaffold.js' ;
1112import { type StoryVariant , scaffoldStoryFile } from '../scaffold-story.js' ;
1213import { buildSpecText , type SpecDeltas } from '../spec-contract.js' ;
14+ import { runTestsAsync } from '../tools/test-runner.js' ;
15+ import { runTypeCheckAsync } from '../tools/type-checker.js' ;
1316import { createComponentBodyAgent } from './component-body-agent.js' ;
1417import { createEvaluationAgent } from './evaluation-agent.js' ;
1518import { LintFixLoopAgent } from './lint-fix-loop-agent.js' ;
@@ -260,16 +263,27 @@ export class ComponentPipelineAgent extends BaseAgent {
260263 } ) ;
261264 ctx . session . events . push ( userEvent ) ;
262265
266+ const logger = new PipelineLogger ( componentName ) ;
267+
263268 debug ( 'Stage 1: Generating component body and tests in parallel' ) ;
269+ logger . stageStart ( 'Stage 1: Component body' ) ;
270+ logger . stageStart ( 'Stage 1: Test generation' ) ;
271+ const llmStart1 = Date . now ( ) ;
264272 const componentGen = this . runSubAgent ( this . componentBodyAgent , ctx ) ;
265273 const testGen = this . runSubAgent ( this . testGenerationAgent , ctx ) ;
266274 const [ componentEvents , testEvents ] = await Promise . all ( [
267275 this . collectEvents ( componentGen ) ,
268276 this . collectEvents ( testGen ) ,
269277 ] ) ;
278+ const llmDuration1 = Date . now ( ) - llmStart1 ;
279+ logger . trackLlmTime ( llmDuration1 ) ;
280+ logger . trackEvents ( componentEvents ) ;
281+ logger . trackEvents ( testEvents ) ;
270282 for ( const event of componentEvents ) yield event ;
271283 for ( const event of testEvents ) yield event ;
272284 this . llmCallCount += 2 ;
285+ logger . stageEnd ( 'Stage 1: Component body' ) ;
286+ logger . stageEnd ( 'Stage 1: Test generation' ) ;
273287
274288 const rawBody = String ( ctx . session . state [ 'component_body' ] ?? '' ) ;
275289 const { body, additionalImports } = sanitizeBodyWithImports ( rawBody , componentName ) ;
@@ -291,58 +305,155 @@ export class ComponentPipelineAgent extends BaseAgent {
291305 writeFileSync ( testPath , testCode , 'utf-8' ) ;
292306
293307 debug ( 'Stage 2: Generating story' ) ;
308+ logger . stageStart ( 'Stage 2: Story generation' ) ;
294309 if ( storyVariants && storyVariants . length > 0 ) {
295310 const storyCode = scaffoldStoryFile ( {
296311 componentName,
297312 componentImportPath,
298313 variants : storyVariants ,
299314 } ) ;
300315 writeFileSync ( storyPath , storyCode , 'utf-8' ) ;
316+ logger . stageEnd ( 'Stage 2: Story generation' , 'scaffolded, no LLM' ) ;
301317 } else {
302318 ctx . session . state [ 'component_code' ] = componentCode ;
303319
320+ const llmStart2 = Date . now ( ) ;
321+ const storyEvents : Event [ ] = [ ] ;
304322 for await ( const event of this . storyAgent . runAsync ( ctx ) ) {
323+ storyEvents . push ( event ) ;
305324 yield event ;
306325 }
326+ logger . trackLlmTime ( Date . now ( ) - llmStart2 ) ;
327+ logger . trackEvents ( storyEvents ) ;
307328 this . llmCallCount ++ ;
308329
309330 const rawStory = String ( ctx . session . state [ 'story_code' ] ?? '' ) ;
310331 const storyCode = extractCodeBlock ( rawStory ) ;
311332 writeFileSync ( storyPath , storyCode , 'utf-8' ) ;
333+ logger . stageEnd ( 'Stage 2: Story generation' ) ;
312334 }
313335
314- debug ( 'Stage 3: Fix loops — type check' ) ;
315- for await ( const event of this . typeFixLoopAgent . runAsync ( ctx ) ) {
316- yield event ;
336+ debug ( 'Stage 3: Parallel initial checks' ) ;
337+ logger . stageStart ( 'Stage 3: Initial checks' ) ;
338+ const checksT0 = Date . now ( ) ;
339+ const [ typeResult , testResult , storyTypeResult ] = await Promise . all ( [
340+ runTypeCheckAsync ( targetDir , [ componentPath , storyPath ] ) ,
341+ runTestsAsync ( testPath , targetDir ) ,
342+ runTypeCheckAsync ( targetDir , [ storyPath ] ) ,
343+ ] ) ;
344+ logger . trackToolTime ( Date . now ( ) - checksT0 ) ;
345+ logger . stageEnd (
346+ 'Stage 3: Initial checks' ,
347+ [
348+ typeResult . passed ? 'types: ok' : `types: ${ typeResult . errors . length } error(s)` ,
349+ testResult . passed ? 'tests: ok' : `tests: ${ testResult . failures . length } failure(s)` ,
350+ storyTypeResult . passed ? 'story types: ok' : `story types: ${ storyTypeResult . errors . length } error(s)` ,
351+ ] . join ( ', ' ) ,
352+ ) ;
353+
354+ if ( ! typeResult . passed ) {
355+ debug ( 'Stage 3: Fix loops — type check' ) ;
356+ logger . stageStart ( 'Stage 3: Type check fix' ) ;
357+ const t0 = Date . now ( ) ;
358+ for await ( const event of this . typeFixLoopAgent . runAsync ( ctx ) ) {
359+ logger . trackTokens ( event ) ;
360+ yield event ;
361+ }
362+ const dur = Date . now ( ) - t0 ;
363+ if ( this . typeFixLoopAgent . iterationsUsed > 0 ) {
364+ logger . trackLlmTime ( dur ) ;
365+ logger . addFixIterations ( this . typeFixLoopAgent . iterationsUsed ) ;
366+ } else {
367+ logger . trackToolTime ( dur ) ;
368+ }
369+ logger . stageEnd ( 'Stage 3: Type check fix' , `${ this . typeFixLoopAgent . iterationsUsed } fix iteration(s)` ) ;
370+ } else {
371+ debug ( 'Stage 3: Type check passed — skipping fix loop' ) ;
317372 }
318373
319- debug ( 'Stage 3: Fix loops — test fix' ) ;
320- for await ( const event of this . testFixLoopAgent . runAsync ( ctx ) ) {
321- yield event ;
374+ if ( ! testResult . passed ) {
375+ debug ( 'Stage 3: Fix loops — test fix' ) ;
376+ logger . stageStart ( 'Stage 3: Test fix' ) ;
377+ const t0 = Date . now ( ) ;
378+ for await ( const event of this . testFixLoopAgent . runAsync ( ctx ) ) {
379+ logger . trackTokens ( event ) ;
380+ yield event ;
381+ }
382+ const dur = Date . now ( ) - t0 ;
383+ if ( this . testFixLoopAgent . iterationsUsed > 0 ) {
384+ logger . trackLlmTime ( dur ) ;
385+ logger . addFixIterations ( this . testFixLoopAgent . iterationsUsed ) ;
386+ } else {
387+ logger . trackToolTime ( dur ) ;
388+ }
389+ logger . stageEnd ( 'Stage 3: Test fix' , `${ this . testFixLoopAgent . iterationsUsed } fix iteration(s)` ) ;
390+ } else {
391+ debug ( 'Stage 3: Test run passed — skipping fix loop' ) ;
322392 }
323393
324- debug ( 'Stage 3: Fix loops — lint fix' ) ;
325- for await ( const event of this . lintFixLoopAgent . runAsync ( ctx ) ) {
326- yield event ;
394+ {
395+ debug ( 'Stage 3: Fix loops — lint fix' ) ;
396+ logger . stageStart ( 'Stage 3: Lint fix' ) ;
397+ const t0 = Date . now ( ) ;
398+ for await ( const event of this . lintFixLoopAgent . runAsync ( ctx ) ) {
399+ logger . trackTokens ( event ) ;
400+ yield event ;
401+ }
402+ const dur = Date . now ( ) - t0 ;
403+ if ( this . lintFixLoopAgent . iterationsUsed > 0 ) {
404+ logger . trackLlmTime ( dur ) ;
405+ logger . addFixIterations ( this . lintFixLoopAgent . iterationsUsed ) ;
406+ } else {
407+ logger . trackToolTime ( dur ) ;
408+ }
409+ logger . stageEnd (
410+ 'Stage 3: Lint fix' ,
411+ this . lintFixLoopAgent . iterationsUsed > 0
412+ ? `${ this . lintFixLoopAgent . iterationsUsed } fix iteration(s)`
413+ : 'passed after auto-fix' ,
414+ ) ;
327415 }
328416
329- debug ( 'Stage 3: Fix loops — story fix' ) ;
330- for await ( const event of this . storyFixLoopAgent . runAsync ( ctx ) ) {
331- yield event ;
417+ if ( ! storyTypeResult . passed ) {
418+ debug ( 'Stage 3: Fix loops — story fix' ) ;
419+ logger . stageStart ( 'Stage 3: Story type check fix' ) ;
420+ const t0 = Date . now ( ) ;
421+ for await ( const event of this . storyFixLoopAgent . runAsync ( ctx ) ) {
422+ logger . trackTokens ( event ) ;
423+ yield event ;
424+ }
425+ const dur = Date . now ( ) - t0 ;
426+ if ( this . storyFixLoopAgent . iterationsUsed > 0 ) {
427+ logger . trackLlmTime ( dur ) ;
428+ logger . addFixIterations ( this . storyFixLoopAgent . iterationsUsed ) ;
429+ } else {
430+ logger . trackToolTime ( dur ) ;
431+ }
432+ logger . stageEnd ( 'Stage 3: Story type check fix' , `${ this . storyFixLoopAgent . iterationsUsed } fix iteration(s)` ) ;
433+ } else {
434+ debug ( 'Stage 3: Story type check passed — skipping fix loop' ) ;
332435 }
333436
334437 debug ( 'Stage 4: Evaluation' ) ;
438+ logger . stageStart ( 'Stage 4: Evaluation' ) ;
335439 ctx . session . state [ 'component_code' ] = readFileSync ( componentPath , 'utf-8' ) ;
336440 ctx . session . state [ 'test_code' ] = readFileSync ( testPath , 'utf-8' ) ;
337441 ctx . session . state [ 'story_code' ] = readFileSync ( storyPath , 'utf-8' ) ;
338442
443+ const llmStart4 = Date . now ( ) ;
339444 for await ( const event of this . evaluationAgent . runAsync ( ctx ) ) {
445+ logger . trackTokens ( event ) ;
340446 yield event ;
341447 }
448+ logger . trackLlmTime ( Date . now ( ) - llmStart4 ) ;
342449 this . llmCallCount ++ ;
343450
344451 this . evaluationResult = this . parseEvaluation ( String ( ctx . session . state [ 'evaluation_result' ] ?? '' ) ) ;
345452
453+ const evalScore = this . evaluationResult ?. totalScore ;
454+ logger . stageEnd ( 'Stage 4: Evaluation' , evalScore != null ? `score: ${ evalScore } ` : undefined ) ;
455+
456+ logger . summary ( ) ;
346457 debug ( 'Pipeline complete. Evaluation score: %d' , this . evaluationResult ?. totalScore ?? - 1 ) ;
347458 }
348459
0 commit comments