@@ -387,7 +387,10 @@ func (w *documentWorkflow) formatExecutionStats(history []map[string]any) string
387387 for i , entry := range history {
388388 nodeName , _ := entry ["node_name" ].(string )
389389 nodeType , _ := entry ["node_type" ].(string )
390- success , _ := entry ["success" ].(bool )
390+ success , ok := entry ["success" ].(bool )
391+ if ! ok {
392+ success = true
393+ }
391394 executionTime , _ := entry ["execution_time" ].(time.Duration )
392395
393396 status := "✅"
@@ -444,7 +447,8 @@ func (w *documentWorkflow) routeComplexity(ctx context.Context, state graph.Stat
444447 // Prefer to pass original text directly to downstream nodes.
445448 // Avoid wrapping to reduce prompt interference for summarizer/enhancer.
446449 var newInput string
447- if orig , ok := state [stateKeyOriginalText ].(string ); ok && strings .TrimSpace (orig ) != "" {
450+ if orig , ok := state [stateKeyOriginalText ].(string ); ok &&
451+ strings .TrimSpace (orig ) != "" {
448452 newInput = orig
449453 } else if in , ok := state [graph .StateKeyUserInput ].(string ); ok {
450454 newInput = in
@@ -455,68 +459,104 @@ func (w *documentWorkflow) routeComplexity(ctx context.Context, state graph.Stat
455459 if newInput != "" {
456460 out [graph .StateKeyUserInput ] = newInput
457461 }
462+ if level := inferComplexityLevel (state ); level != "" {
463+ out [stateKeyComplexityLevel ] = level
464+ }
458465 return out , nil
459466}
460467
461- func (w * documentWorkflow ) complexityCondition (ctx context.Context , state graph.State ) (level string , err error ) {
462- // Ensure we persist whatever we decide for downstream formatting.
463- defer func () { state [stateKeyComplexityLevel ] = level }()
468+ func (w * documentWorkflow ) complexityCondition (
469+ ctx context.Context ,
470+ state graph.State ,
471+ ) (string , error ) {
472+ level := inferComplexityLevel (state )
473+ if level == "" {
474+ level = complexitySimple
475+ }
476+ return level , nil
477+ }
464478
465- // 1) Prefer tool-derived result when present (most reliable)
466- if msgs , ok := state [ graph . StateKeyMessages ].([]model. Message ); ok {
467- for i := len ( msgs ) - 1 ; i >= 0 ; i -- { // scan backwards for latest tool result
468- msg := msgs [ i ]
469- if msg . Role != model . RoleTool {
470- continue
471- }
472- var result complexityResult
473- if err := json . Unmarshal ([] byte ( msg . Content ), & result ); err == nil {
474- switch strings . ToLower ( strings . TrimSpace ( result . Level )) {
475- case complexityComplex :
476- return complexityComplex , nil
477- case complexityModerate :
478- return complexityModerate , nil
479- case complexitySimple :
480- return complexitySimple , nil
481- }
482- }
479+ const (
480+ complexityWordThresholdModerate = 50
481+ complexityWordThresholdComplex = 200
482+ )
483+
484+ func inferComplexityLevel ( state graph. State ) string {
485+ // 1) Prefer tool-derived result when present (most reliable).
486+ if level := inferComplexityFromTools ( state ); level != "" {
487+ return level
488+ }
489+ // 2) Then try to parse the LLM textual response.
490+ if level := inferComplexityFromText ( state ); level != "" {
491+ return level
492+ }
493+ // 3) Final fallback: heuristic on word count.
494+ if wordCount , ok := state [ stateKeyWordCount ].( int ); ok {
495+ if wordCount > complexityWordThresholdComplex {
496+ return complexityComplex
483497 }
498+ if wordCount > complexityWordThresholdModerate {
499+ return complexityModerate
500+ }
501+ return complexitySimple
484502 }
503+ return ""
504+ }
485505
486- // 2) Try to parse the LLM textual response robustly
487- if lastResponse , ok := state [graph .StateKeyLastResponse ].(string ); ok {
488- normalized := strings .ToLower (strings .TrimSpace (lastResponse ))
489- // Try exact token match first
490- switch normalized {
491- case complexitySimple :
492- return complexitySimple , nil
493- case complexityModerate :
494- return complexityModerate , nil
495- case complexityComplex :
496- return complexityComplex , nil
497- }
498- // Fallback: contains any (no surrounding spaces requirement)
499- if strings .Contains (normalized , "complex" ) {
500- return complexityComplex , nil
506+ func inferComplexityFromTools (state graph.State ) string {
507+ msgs , ok := state [graph .StateKeyMessages ].([]model.Message )
508+ if ! ok {
509+ return ""
510+ }
511+ for i := len (msgs ) - 1 ; i >= 0 ; i -- {
512+ msg := msgs [i ]
513+ if msg .Role != model .RoleTool {
514+ continue
501515 }
502- if strings .Contains (normalized , "moderate" ) {
503- return complexityModerate , nil
516+ var result complexityResult
517+ if err := json .Unmarshal ([]byte (msg .Content ), & result ); err != nil {
518+ continue
504519 }
505- if strings . Contains ( normalized , "simple" ) {
506- return complexitySimple , nil
520+ if level := normalizeComplexityLevel ( result . Level ); level != "" {
521+ return level
507522 }
508523 }
524+ return ""
525+ }
509526
510- // 3) Final fallback: heuristic on word count
511- const complexityThreshold = 200
512- if wordCount , ok := state [stateKeyWordCount ].(int ); ok {
513- if wordCount > complexityThreshold {
514- return complexityComplex , nil
515- } else if wordCount > 50 {
516- return complexityModerate , nil
517- }
527+ func inferComplexityFromText (state graph.State ) string {
528+ lastResponse , ok := state [graph .StateKeyLastResponse ].(string )
529+ if ! ok {
530+ return ""
531+ }
532+ normalized := strings .ToLower (strings .TrimSpace (lastResponse ))
533+ if level := normalizeComplexityLevel (normalized ); level != "" {
534+ return level
535+ }
536+ if strings .Contains (normalized , complexityComplex ) {
537+ return complexityComplex
538+ }
539+ if strings .Contains (normalized , complexityModerate ) {
540+ return complexityModerate
541+ }
542+ if strings .Contains (normalized , complexitySimple ) {
543+ return complexitySimple
544+ }
545+ return ""
546+ }
547+
548+ func normalizeComplexityLevel (raw string ) string {
549+ level := strings .ToLower (strings .TrimSpace (raw ))
550+ switch level {
551+ case complexitySimple :
552+ return complexitySimple
553+ case complexityModerate :
554+ return complexityModerate
555+ case complexityComplex :
556+ return complexityComplex
557+ default :
558+ return ""
518559 }
519- return complexitySimple , nil
520560}
521561
522562func (w * documentWorkflow ) formatOutput (ctx context.Context , state graph.State ) (any , error ) {
@@ -545,6 +585,9 @@ func (w *documentWorkflow) formatOutput(ctx context.Context, state graph.State)
545585 }
546586 // Create final formatted output.
547587 complexityLevel , _ := state [stateKeyComplexityLevel ].(string )
588+ if strings .TrimSpace (complexityLevel ) == "" {
589+ complexityLevel = inferComplexityLevel (state )
590+ }
548591 wordCount , _ := state [stateKeyWordCount ].(int )
549592
550593 // Extract callback-generated metadata for enhanced output.
@@ -693,6 +736,7 @@ func (w *documentWorkflow) processStreamingResponse(eventChan <-chan *event.Even
693736 // Track if we are between analyze and next hop to assert tool usage
694737 inAnalyze bool
695738 toolSeenForThisAnalyze bool
739+ completionEvent * event.Event
696740 )
697741 for event := range eventChan {
698742 // Handle errors.
@@ -835,14 +879,53 @@ func (w *documentWorkflow) processStreamingResponse(eventChan <-chan *event.Even
835879 }
836880 }
837881 // Stage counting now handled on node-complete events for clarity.
838- // Handle completion.
839- if event . Done {
882+ if event . IsRunnerCompletion () {
883+ completionEvent = event
840884 break
841885 }
842886 }
887+ if completionEvent == nil {
888+ return nil
889+ }
890+ if workflowStarted {
891+ fmt .Println ()
892+ }
893+ finalOutput := finalOutputFromEvent (completionEvent )
894+ if strings .TrimSpace (finalOutput ) == "" {
895+ return nil
896+ }
897+ fmt .Println (finalOutput )
843898 return nil
844899}
845900
901+ func finalOutputFromEvent (e * event.Event ) string {
902+ if e == nil || e .Response == nil {
903+ return ""
904+ }
905+
906+ if len (e .Response .Choices ) > 0 {
907+ content := e .Response .Choices [0 ].Message .Content
908+ if strings .TrimSpace (content ) != "" {
909+ return content
910+ }
911+ }
912+
913+ if e .StateDelta == nil {
914+ return ""
915+ }
916+
917+ data , ok := e .StateDelta [graph .StateKeyLastResponse ]
918+ if ! ok {
919+ return ""
920+ }
921+
922+ var text string
923+ if err := json .Unmarshal (data , & text ); err != nil {
924+ return ""
925+ }
926+ return text
927+ }
928+
846929// showHelp displays available commands.
847930func (w * documentWorkflow ) showHelp () {
848931 fmt .Println ("📚 Available Commands:" )
0 commit comments