Module: PWN::AI::Agent::TurnFinalizer
- Defined in:
- lib/pwn/ai/agent/turn_finalizer.rb
Overview
Hermes-style post-reply learning. Loop.run returns the user-visible answer first; Reward / Policy / Curriculum keep running off that path.
User-visible turn (Loop.run) tools, streaming, TaskSummarizer, final text After the reply is already decided TurnFinalizer.defer -> daemon thread re-attaches the R5 episode snapshot Learning.auto_introspect (critic, Reward.judge, PRM, HER, reflect)
Default ON during Loop.run (PWN::Env[:agent][:defer_introspect]). Direct Learning.auto_introspect calls (specs, cron, tools) stay inline so existing ORM contracts still get a synchronous return value.
Constant Summary collapse
- MUTEX =
Mutex.new
- THREADS =
rubocop:disable Style/MutableConstant
[]
- MAX_TRACKED =
16- DEPTH_KEY =
:pwn_turn_finalizer_depth- INLINE_KEY =
:pwn_turn_finalizer_inline- SYNC_KEY =
:pwn_turn_finalizer_sync
Class Method Summary collapse
- .authors ⇒ Object
-
.defer(opts = {}) ⇒ Object
- Supported Method Parameters
result = PWN::AI::Agent::TurnFinalizer.defer( session_id: 'required - PWN::Sessions id', request: 'optional - original user request', final: 'optional - user-visible final answer', predicted: 'optional - W3 predicted score', plan: 'optional - tangible-task plan', ts_state: 'optional - TaskSummarizer state' ).
-
.enter_user_path! ⇒ Object
- Supported Method Parameters
PWN::AI::Agent::TurnFinalizer.enter_user_path!.
-
.finalize(opts = {}) ⇒ Object
- Supported Method Parameters
result = PWN::AI::Agent::TurnFinalizer.finalize( session_id:, request:, final:, predicted:, plan:, ts_state:, should: true|false ).
- .help ⇒ Object
-
.join_all!(opts = {}) ⇒ Object
- Supported Method Parameters
n = PWN::AI::Agent::TurnFinalizer.join_all!(timeout: 15).
-
.leave_user_path! ⇒ Object
- Supported Method Parameters
PWN::AI::Agent::TurnFinalizer.leave_user_path!.
-
.pending ⇒ Object
- Supported Method Parameters
n = PWN::AI::Agent::TurnFinalizer.pending.
-
.should_defer? ⇒ Boolean
- Supported Method Parameters
ok = PWN::AI::Agent::TurnFinalizer.should_defer?.
- .user_path? ⇒ Boolean
Class Method Details
.authors ⇒ Object
179 180 181 |
# File 'lib/pwn/ai/agent/turn_finalizer.rb', line 179 public_class_method def self. "AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n" end |
.defer(opts = {}) ⇒ Object
- Supported Method Parameters
result = PWN::AI::Agent::TurnFinalizer.defer( session_id: 'required - PWN::Sessions id', request: 'optional - original user request', final: 'optional - user-visible final answer', predicted: 'optional - W3 predicted score', plan: 'optional - tangible-task plan', ts_state: 'optional - TaskSummarizer state' )
Snapshots the live Policy episode onto this thread, clears it so Loop.maybe_finish_policy is a no-op, and runs Learning.auto_introspect on a daemon thread with the episode re-attached.
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 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/pwn/ai/agent/turn_finalizer.rb', line 76 public_class_method def self.defer(opts = {}) session_id = opts[:session_id] return { skipped: :no_session } if session_id.to_s.strip.empty? return { skipped: :no_learning } unless defined?(Learning) episode = nil episode = Policy.detach_episode! if defined?(Policy) && Policy.respond_to?(:detach_episode!) payload = { session_id: session_id, request: opts[:request], final: opts[:final], predicted: opts[:predicted], plan: opts[:plan], ts_state: opts[:ts_state], inline: true } worker = Thread.new do Thread.current[INLINE_KEY] = true begin Policy.attach_episode!(episode: episode) if defined?(Policy) && Policy.respond_to?(:attach_episode!) Learning.auto_introspect(payload) rescue StandardError => e warn "[pwn-ai/turn_finalizer] background review swallowed: #{e.class}: #{e.}" nil ensure leftover = defined?(Policy) && Policy.respond_to?(:current_episode) && Policy.current_episode if leftover && Policy.respond_to?(:finish) Policy.finish( session_id: session_id, proxy_ok: false, final: payload[:final], ts_state: payload[:ts_state] ) elsif defined?(Policy) && Policy.respond_to?(:attach_episode!) Policy.attach_episode!(episode: nil) end end end worker.report_on_exception = false begin worker.name = 'pwn-turn-finalizer' rescue StandardError nil end track(thr: worker) { deferred: true, session_id: session_id, thread_id: worker.object_id } rescue StandardError => e warn "[pwn-ai/turn_finalizer] defer swallowed: #{e.class}: #{e.}" # Never drop the learner — fall back to inline review. opts_inline = opts.merge(inline: true) defined?(Learning) ? Learning.auto_introspect(opts_inline) : { error: e. } end |
.enter_user_path! ⇒ Object
- Supported Method Parameters
PWN::AI::Agent::TurnFinalizer.enter_user_path!
30 31 32 |
# File 'lib/pwn/ai/agent/turn_finalizer.rb', line 30 public_class_method def self.enter_user_path! Thread.current[DEPTH_KEY] = Thread.current[DEPTH_KEY].to_i + 1 end |
.finalize(opts = {}) ⇒ Object
- Supported Method Parameters
result = PWN::AI::Agent::TurnFinalizer.finalize( session_id:, request:, final:, predicted:, plan:, ts_state:, should: true|false )
Single entry used when a caller wants the Hermes split without going through Learning.auto_introspect's gate.
140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/pwn/ai/agent/turn_finalizer.rb', line 140 public_class_method def self.finalize(opts = {}) return { skipped: :gated } unless opts.fetch(:should, true) return { skipped: :no_learning } unless defined?(Learning) if should_defer? defer(opts) else Learning.auto_introspect(opts.merge(inline: true)) end rescue StandardError => e warn "[pwn-ai/turn_finalizer] finalize swallowed: #{e.class}: #{e.}" nil end |
.help ⇒ Object
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
# File 'lib/pwn/ai/agent/turn_finalizer.rb', line 183 public_class_method def self.help puts <<~USAGE USAGE: # Called from Learning.auto_introspect when Loop.run is on the # stack. You almost never invoke this directly. PWN::AI::Agent::TurnFinalizer.defer( session_id: sid, request: req, final: text, plan: plan, ts_state: ts ) # Specs / cron that need the learner to finish before assert: PWN::AI::Agent::TurnFinalizer.join_all!(timeout: 15) Toggle via PWN::Env[:ai][:agent][:defer_introspect] true (default) — Hermes split: learn after the reply false — legacy inline auto_introspect #{self}.authors USAGE end |
.join_all!(opts = {}) ⇒ Object
- Supported Method Parameters
n = PWN::AI::Agent::TurnFinalizer.join_all!(timeout: 15)
157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/pwn/ai/agent/turn_finalizer.rb', line 157 public_class_method def self.join_all!(opts = {}) timeout = (opts[:timeout] || 15).to_f list = MUTEX.synchronize do pending = THREADS.dup THREADS.clear pending end list.each do |thr| thr.join(timeout) if thr.alive? rescue StandardError nil end list.length end |
.leave_user_path! ⇒ Object
- Supported Method Parameters
PWN::AI::Agent::TurnFinalizer.leave_user_path!
37 38 39 40 |
# File 'lib/pwn/ai/agent/turn_finalizer.rb', line 37 public_class_method def self.leave_user_path! depth = Thread.current[DEPTH_KEY].to_i - 1 Thread.current[DEPTH_KEY] = depth.positive? ? depth : 0 end |
.pending ⇒ Object
- Supported Method Parameters
n = PWN::AI::Agent::TurnFinalizer.pending
175 176 177 |
# File 'lib/pwn/ai/agent/turn_finalizer.rb', line 175 public_class_method def self.pending MUTEX.synchronize { THREADS.count(&:alive?) } end |
.should_defer? ⇒ Boolean
- Supported Method Parameters
ok = PWN::AI::Agent::TurnFinalizer.should_defer?
49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/pwn/ai/agent/turn_finalizer.rb', line 49 public_class_method def self.should_defer? return false if Thread.current[INLINE_KEY] return false if Thread.current[SYNC_KEY] return false unless user_path? flag = agent_flag(key: :defer_introspect, default: true) return false if flag == false || flag.to_s.match?(/\A(0|false|no|off)\z/i) true rescue StandardError false end |
.user_path? ⇒ Boolean
42 43 44 |
# File 'lib/pwn/ai/agent/turn_finalizer.rb', line 42 public_class_method def self.user_path? Thread.current[DEPTH_KEY].to_i.positive? end |