Class: RubyLLM::Team::Session

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby_llm/team.rb

Overview

Preserves coworker results, limits calls, and exposes a trace for one run.

Defined Under Namespace

Classes: Call

Instance Method Summary collapse

Constructor Details

#initialize(agents, max_calls:, share_context:, context:) ⇒ Session

Returns a new instance of Session.



72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/ruby_llm/team.rb', line 72

def initialize(agents, max_calls:, share_context:, context:)
  validate_max_calls(max_calls)

  @agents = agents
  @max_calls = max_calls
  @share_context = share_context
  @context = context&.to_s&.dup&.freeze
  @calls = []
  @accepted_calls = 0
  @mutex = Mutex.new
  initialize_run_state
  initialize_agent_mutexes(agents)
end

Instance Method Details

#artifact(name) ⇒ Object



113
114
115
# File 'lib/ruby_llm/team.rb', line 113

def artifact(name)
  @mutex.synchronize { @artifacts.fetch(name.to_s, []).last }
end

#artifacts(name) ⇒ Object



117
118
119
# File 'lib/ruby_llm/team.rb', line 117

def artifacts(name)
  @mutex.synchronize { @artifacts.fetch(name.to_s, []).dup.freeze }
end

#ask(coworker, prompt, as: nil, from: nil) ⇒ Object



91
92
93
94
95
96
# File 'lib/ruby_llm/team.rb', line 91

def ask(coworker, prompt, as: nil, from: nil)
  result = consult(
    action: 'delegate_work', prompt: prompt, coworker: coworker, as: as, from: from
  )
  raise_on_error(result)
end

#callsObject



105
# File 'lib/ruby_llm/team.rb', line 105

def calls = @mutex.synchronize { @calls.dup.freeze }

#calls_remainingObject

Calls still allowed by the budget, or nil when the session is unbounded. Lets an application decide whether an optional pass still fits.



109
110
111
# File 'lib/ruby_llm/team.rb', line 109

def calls_remaining
  @mutex.synchronize { @max_calls && [@max_calls - @accepted_calls, 0].max }
end

#collaboration_toolsObject Also known as: tools



86
87
88
# File 'lib/ruby_llm/team.rb', line 86

def collaboration_tools
  [DelegateWork.new(self), AskQuestion.new(self)]
end

#consult(action:, prompt:, coworker:, as: nil, from: nil) ⇒ Object



156
157
158
159
# File 'lib/ruby_llm/team.rb', line 156

def consult(action:, prompt:, coworker:, as: nil, from: nil)
  role = coworker.to_s
  perform(reserve(action, role, prompt, as: as || role, from: from), coworker)
end

#coworkersObject



154
# File 'lib/ruby_llm/team.rb', line 154

def coworkers = @agents.keys.join(', ')

#parallel(tasks, concurrency: :threads, from: nil) ⇒ Object



98
99
100
101
102
103
# File 'lib/ruby_llm/team.rb', line 98

def parallel(tasks, concurrency: :threads, from: nil)
  runner = parallel_runner(concurrency)
  reject_duplicate_roles(tasks)
  work = reserve_batch(tasks, from)
  send(runner, work).transform_values { |result| raise_on_error(result) }
end

#to_h(include_content: false) ⇒ Object

Machine-readable trace: structure and best-known usage by default; pass include_content: true to also export prompts and results.



138
139
140
141
142
143
144
145
146
147
# File 'lib/ruby_llm/team.rb', line 138

def to_h(include_content: false)
  # One snapshot under one lock: calls and artifacts must not disagree.
  @mutex.synchronize do
    {
      calls: @calls.each_with_index.map { |call, index| call_to_h(call, index, include_content) },
      artifacts: artifacts_to_h,
      usage: usage_totals(@calls)
    }
  end
end

#to_json(*_args, include_content: false) ⇒ Object

Accepts JSON's positional generator state so JSON.generate(session) works.



150
151
152
# File 'lib/ruby_llm/team.rb', line 150

def to_json(*_args, include_content: false)
  JSON.generate(to_h(include_content: include_content))
end

#to_markdownObject



123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/ruby_llm/team.rb', line 123

def to_markdown
  trace = calls.map.with_index(1) do |call, index|
    result = call.complete? ? format_result(call.result) : '_In progress_'
    inputs = call.inputs.empty? ? '_None_' : call.inputs.join(', ')
    # The whole prompt, handoffs included: "what was actually sent" is the point of
    # the trace, so the readable format must not be the one that hides it.
    "## #{index}. #{call.coworker} via #{call.action}\n\n" \
      "### Inputs\n\n#{inputs}\n\n### Request\n\n#{call.prompt}\n\n" \
      "### Result\n\n#{result}"
  end.join("\n\n")
  @context ? "## Shared team context\n\n#{@context}\n\n#{trace}" : trace
end

#value(name) ⇒ Object



121
# File 'lib/ruby_llm/team.rb', line 121

def value(name) = artifact(name)&.value