Module: Legion::Extensions::Developer::Runners::Developer

Extended by:
Developer
Included in:
Developer
Defined in:
lib/legion/extensions/developer/runners/developer.rb

Instance Method Summary collapse

Instance Method Details

#implement(results: nil, work_item: nil, args: nil) ⇒ Object

Raises:

  • (ArgumentError)


12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/legion/extensions/developer/runners/developer.rb', line 12

def implement(results: nil, work_item: nil, args: nil, **)
  results = Legion::JSON.load(results) if results.is_a?(String) # rubocop:disable Legion/HelperMigration/DirectJson
  work_item ||= results&.dig(:work_item) || args&.dig(:work_item)
  raise ArgumentError, "work_item is nil in #{__method__}" if work_item.nil?

  started_at = Time.now.utc.iso8601

  materialization = if work_item[:pipeline][:attempt].to_i.positive? && work_item[:pipeline][:branch_name]
                      reuse_worktree(work_item: work_item)
                    else
                      token = Legion::Settings.dig(:fleet, :github, :token)
                      Exec::Helpers::RepoMaterializer.materialize(
                        work_item:           work_item,
                        credential_provider: token
                      )
                    end

  unless materialization[:success]
    return { success: false, error: :materialization_failed,
             message: materialization[:reason].to_s }
  end

  prompt = Helpers::PromptBuilder.build_implementation_prompt(work_item: work_item)
  llm_response = call_llm(work_item: work_item, prompt: prompt)

  changes = Helpers::ChangeParser.parse(content: llm_response[:content])
  file_paths = Helpers::ChangeParser.file_paths_only(changes: changes)

  worktree_path = materialization[:worktree_path] || materialization[:repo_path]
  write_changes(changes: changes, worktree_path: worktree_path)

  Exec::Runners::Git.add(path: worktree_path, files: file_paths)
  Exec::Runners::Git.commit(path: worktree_path, message: "fleet: #{work_item[:title]}")
  Exec::Runners::Git.push(path: worktree_path, branch: materialization[:branch])

  pr_result = if work_item.dig(:pipeline, :pr_number)
                { number: work_item.dig(:pipeline, :pr_number) }
              else
                create_pull_request(work_item: work_item, branch: materialization[:branch],
                                    file_paths: file_paths)
              end

  work_item = work_item.merge(
    pipeline: work_item[:pipeline].merge(
      stage:       'implemented',
      changes:     file_paths,
      branch_name: materialization[:branch],
      pr_number:   pr_result&.dig(:number),
      trace:       work_item[:pipeline][:trace] + [build_trace_entry(started_at: started_at)]
    )
  )

  { success: true, work_item: work_item, changes: changes }
end

#incorporate_feedback(results: nil, work_item: nil, args: nil) ⇒ Object

Raises:

  • (ArgumentError)


67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/legion/extensions/developer/runners/developer.rb', line 67

def incorporate_feedback(results: nil, work_item: nil, args: nil, **)
  results = Legion::JSON.load(results) if results.is_a?(String) # rubocop:disable Legion/HelperMigration/DirectJson
  work_item ||= results&.dig(:work_item) || args&.dig(:work_item)
  raise ArgumentError, "work_item is nil in #{__method__}" if work_item.nil?

  max = work_item.dig(:config, :implementation, :max_iterations) ||
        Legion::Settings.dig(:fleet, :implementation, :max_iterations) || 5

  if work_item[:pipeline][:attempt] >= max - 1
    return {
      success:   true,
      work_item: work_item.merge(pipeline: work_item[:pipeline].merge(stage: 'escalated')),
      escalate:  true
    }
  end

  feedback_history = Array(work_item.dig(:pipeline, :feedback_history))
  summarize_after = work_item.dig(:config, :feedback, :summarize_after) || 2

  if Helpers::FeedbackSummarizer.needs_summarization?(
    feedback_history: feedback_history, threshold: summarize_after
  )
    feedback_history = Helpers::FeedbackSummarizer.summarize(feedback_history: feedback_history)
  end

  new_attempt = (work_item.dig(:pipeline, :attempt) || 0) + 1

  work_item = work_item.merge(
    pipeline: work_item[:pipeline].merge(
      attempt:          new_attempt,
      feedback_history: feedback_history
    )
  )

  implement(work_item: work_item)
end