Class: Agentilda::Tally

Inherits:
Object
  • Object
show all
Defined in:
lib/agentilda/tally.rb

Overview

What a run cost, once it is over.

The loop reports movement โ€” which plan went from โญ๏ธ to ๐ŸŸก, which agent failed and why โ€” and says nothing about the bill. A round of five agents against a large repository spends several million tokens and the better part of an hour, and until this existed the only record of that was a directory of trace files nobody reads.

#render returns a string and prints nothing, so the caller decides where it goes, the same way Reporter works.

Defined Under Namespace

Classes: Row

Constant Summary collapse

WIDTHS =

Cells per column, so the header, the rows and the rule cannot drift.

{name: 22, invocations: 6, subagents: 11, up: 9, down: 9, seconds: 8}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attempts:, seconds:, rounds: 0) ⇒ Tally

Returns a new instance of Tally.

Parameters:

  • attempts (Array<Agentilda::Runner::Attempt>)
  • seconds (Float)

    wall clock for the whole loop, which is not the sum of the agents' own times: several of them run at once

  • rounds (Integer) (defaults to: 0)


38
39
40
41
42
# File 'lib/agentilda/tally.rb', line 38

def initialize(attempts:, seconds:, rounds: 0)
  @attempts = attempts.reject { |a| a.ordinal == "?" }
  @seconds = seconds
  @rounds = rounds
end

Instance Attribute Details

#attemptsArray<Agentilda::Runner::Attempt> (readonly)

Returns:



45
46
47
# File 'lib/agentilda/tally.rb', line 45

def attempts
  @attempts
end

#roundsInteger (readonly)

Returns:

  • (Integer)


51
52
53
# File 'lib/agentilda/tally.rb', line 51

def rounds
  @rounds
end

#secondsFloat (readonly)

Returns wall clock.

Returns:

  • (Float)

    wall clock



48
49
50
# File 'lib/agentilda/tally.rb', line 48

def seconds
  @seconds
end

Instance Method Details

#busyFloat

Returns seconds of agent time, added up across all of them.

Returns:

  • (Float)

    seconds of agent time, added up across all of them



76
# File 'lib/agentilda/tally.rb', line 76

def busy = attempts.sum(&:seconds)

#by_agentArray<Agentilda::Tally::Row>

Returns one per agent, dearest first.

Returns:



86
87
88
89
90
91
# File 'lib/agentilda/tally.rb', line 86

def by_agent
  attempts.group_by(&:agent).map { |name, list|
    Row.new(name:, invocations: list.size, subagents: list.sum(&:subagents),
      up: list.sum(&:up), down: list.sum(&:down), seconds: list.sum(&:seconds))
  }.sort_by { |row| -row.up }
end

#concurrencyFloat

How many agents were running at any given moment, averaged over the run: agent-seconds divided by wall-clock seconds. Below the -j ceiling by however much the round spent waiting for its slowest member.

Returns:

  • (Float)


83
# File 'lib/agentilda/tally.rb', line 83

def concurrency = seconds.positive? ? busy / seconds : 0.0

#delegatedInteger

Sub-agent spend that arrived as one number. claude reports what a sub-agent cost without splitting it between the two directions, so this much of #up is really up and down together.

Returns:

  • (Integer)


73
# File 'lib/agentilda/tally.rb', line 73

def delegated = attempts.sum(&:delegated)

#downInteger

Returns tokens generated.

Returns:

  • (Integer)

    tokens generated



63
# File 'lib/agentilda/tally.rb', line 63

def down = attempts.sum(&:down)

#plansInteger

Plans an agent was actually started against, counted once each however many rounds they took.

Returns:

  • (Integer)


57
# File 'lib/agentilda/tally.rb', line 57

def plans = attempts.map(&:ordinal).uniq.size

#renderString

Returns the table and the summary line under it.

Returns:

  • (String)

    the table and the summary line under it



94
95
96
97
98
# File 'lib/agentilda/tally.rb', line 94

def render
  return summary if attempts.empty?

  ([header, rule] + by_agent.map { |row| line(row) } + [rule, totals, "", summary]).join("\n")
end

#subagentsInteger

Returns sub-agents spawned by every agent in the run.

Returns:

  • (Integer)

    sub-agents spawned by every agent in the run



66
# File 'lib/agentilda/tally.rb', line 66

def subagents = attempts.sum(&:subagents)

#summaryString

Returns the one-line version, for a box.

Returns:

  • (String)

    the one-line version, for a box



101
102
103
104
105
106
107
108
109
# File 'lib/agentilda/tally.rb', line 101

def summary
  [
    "#{plans} plan#{"s" unless plans == 1} addressed",
    "#{rounds} round#{"s" unless rounds == 1}",
    duration(seconds),
    "#{format("%.1f", concurrency)} agents at a time",
    "โ†‘ #{UI.abbreviate(up)} โ†“ #{UI.abbreviate(down)}"
  ].join(" ยท ") + delegation
end

#upInteger

Returns tokens sent, everything included.

Returns:

  • (Integer)

    tokens sent, everything included



60
# File 'lib/agentilda/tally.rb', line 60

def up = attempts.sum(&:up)