Skip to content

Commit c8c2594

Browse files
authored
examples: fix format output of graph basic example (#749)
1 parent 6e65141 commit c8c2594

File tree

1 file changed

+136
-53
lines changed

1 file changed

+136
-53
lines changed

examples/graph/basic/main.go

Lines changed: 136 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -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

522562
func (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.
847930
func (w *documentWorkflow) showHelp() {
848931
fmt.Println("📚 Available Commands:")

0 commit comments

Comments
 (0)