Class: LittleGhost::RunContext

Inherits:
Object
  • Object
show all
Defined in:
lib/little_ghost/run_context.rb

Overview

RunContext gives tools and workflows one place for shared state, cancellation, deadlines, checkpoints, and accumulated usage. It travels with work inside a run without becoming global process state.

Tools and workflows use it to share JSON-like state, check cancellation and deadlines, checkpoint messages, and accumulate usage. Access to framework-managed fields is thread-safe.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(state: {}, cancellation_token: Support::CancellationToken.new, deadline: nil, metadata: {}, checkpoint: nil, conversation_id: nil, interjection_metadata: nil, interjection_ids: []) ⇒ RunContext

Creates a context with optional checkpoint and interjection state.



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
# File 'lib/little_ghost/run_context.rb', line 31

def initialize(
  state: {},
  cancellation_token: Support::CancellationToken.new,
  deadline: nil,
  metadata: {},
  checkpoint: nil,
  conversation_id: nil,
  interjection_metadata: nil,
  interjection_ids: []
)
  if conversation_id
    conversation_id = String(conversation_id)
    raise ArgumentError, "conversation_id cannot be empty" if conversation_id.empty?
    conversation_id = conversation_id.dup.freeze
  end
  @state = DataMap.new(state)
  @cancellation_token = cancellation_token
  @deadline = deadline
  @metadata = .freeze
  @checkpoint = checkpoint
  @conversation_id = conversation_id
  @usage = Usage.new
  @usage_mutex = Mutex.new
  @structured_result = nil
  @structured_result_mutex = Mutex.new
  @agent_operation_id = nil
  @agent_operation_id_mutex = Mutex.new
  @interjection_mutex = Mutex.new
  @interjection_metadata = &.to_h
  @interjection_ids = Array(interjection_ids).map { |id| String(id).dup.freeze }.freeze
end

Instance Attribute Details

#agent_operation_idObject (readonly)

Active Agent operation identifier, after the context is bound.



26
27
28
# File 'lib/little_ghost/run_context.rb', line 26

def agent_operation_id
  @agent_operation_id
end

#cancellation_tokenObject (readonly)

Token used to cooperatively stop the current work.



20
21
22
# File 'lib/little_ghost/run_context.rb', line 20

def cancellation_token
  @cancellation_token
end

#conversation_idObject (readonly)

Durable subagent conversation identifier, when present.



28
29
30
# File 'lib/little_ghost/run_context.rb', line 28

def conversation_id
  @conversation_id
end

#deadlineObject (readonly)

Wall-clock deadline for the current work, when present.



22
23
24
# File 'lib/little_ghost/run_context.rb', line 22

def deadline
  @deadline
end

#metadataObject (readonly)

Framework metadata attached to this context.



24
25
26
# File 'lib/little_ghost/run_context.rb', line 24

def 
  @metadata
end

#stateObject (readonly)

Mutable DataMap state supplied to this invocation. A top-level Run starts with restored Session state merged with current Invocation context; child Assemblies may receive copied, mapped, or empty state. Application code must synchronize mutations when parallel Tools share this map, or use exclusive Tools. String and Symbol keys address the same value; persisted snapshots use canonical String keys.



18
19
20
# File 'lib/little_ghost/run_context.rb', line 18

def state
  @state
end

Instance Method Details

#activate_interjection(metadata:, ids:) ⇒ Object

:nodoc:



123
124
125
126
127
128
129
130
# File 'lib/little_ghost/run_context.rb', line 123

def activate_interjection(metadata:, ids:) # :nodoc:
  value = &.to_h
  values = Array(ids).map { |id| String(id).dup.freeze }.freeze
  @interjection_mutex.synchronize do
    @interjection_metadata = value
    @interjection_ids = values
  end
end

#bind_agent_operation_id(operation_id) ⇒ Object

:nodoc:



132
133
134
135
136
137
138
139
140
# File 'lib/little_ghost/run_context.rb', line 132

def bind_agent_operation_id(operation_id) # :nodoc:
  @agent_operation_id_mutex.synchronize do
    if @agent_operation_id && @agent_operation_id != operation_id
      raise Error, "run context is already bound to an agent operation"
    end

    @agent_operation_id ||= operation_id
  end
end

#check!Object

Raises LittleGhost::CancelledError or LittleGhost::DeadlineExceededError when execution should stop.



65
66
67
68
# File 'lib/little_ghost/run_context.rb', line 65

def check!
  cancellation_token.raise_if_cancelled!
  raise DeadlineExceededError, "The run deadline was reached" if deadline && Time.now >= deadline
end

#checkpoint(messages) ⇒ Object

Sends messages and current state to the configured checkpoint callback. With no checkpoint callback, this method does nothing and returns nil.



72
73
74
75
76
77
78
79
80
# File 'lib/little_ghost/run_context.rb', line 72

def checkpoint(messages)
  return unless @checkpoint

  if agent_operation_id
    @checkpoint.call(messages:, state:, parent_operation_id: agent_operation_id)
  else
    @checkpoint.call(messages:, state:)
  end
end

#interjection_idsObject

:nodoc:



119
120
121
# File 'lib/little_ghost/run_context.rb', line 119

def interjection_ids # :nodoc:
  @interjection_mutex.synchronize { @interjection_ids }
end

#interjection_metadataObject

:nodoc:



115
116
117
# File 'lib/little_ghost/run_context.rb', line 115

def  # :nodoc:
  @interjection_mutex.synchronize { @interjection_metadata }
end

#record_usage(value) ⇒ Object

Adds value to accumulated model usage.



83
84
85
# File 'lib/little_ghost/run_context.rb', line 83

def record_usage(value)
  @usage_mutex.synchronize { @usage += value }
end

#remaining_time(maximum = nil) ⇒ Object

Calculates seconds remaining before the deadline.

When maximum is provided, the result is capped at that value. With no deadline, returns maximum.



96
97
98
99
100
101
102
# File 'lib/little_ghost/run_context.rb', line 96

def remaining_time(maximum = nil)
  check!
  return maximum unless deadline

  remaining = deadline - Time.now
  maximum ? [remaining, maximum].min : remaining
end

#structured_resultObject

Finds the latest validated structured result, if any.



111
112
113
# File 'lib/little_ghost/run_context.rb', line 111

def structured_result
  @structured_result_mutex.synchronize { @structured_result }
end

#submit_structured_result(result) ⇒ Object

Stores a validated LittleGhost::StructuredResult and returns it.



105
106
107
108
# File 'lib/little_ghost/run_context.rb', line 105

def submit_structured_result(result)
  @structured_result_mutex.synchronize { @structured_result = result }
  result
end

#usageObject

Takes a snapshot of accumulated usage.



88
89
90
# File 'lib/little_ghost/run_context.rb', line 88

def usage
  @usage_mutex.synchronize { @usage }
end