Class: Vangrail::Tools
- Inherits:
-
Object
- Object
- Vangrail::Tools
- Defined in:
- lib/vangrail/tools.rb
Overview
Named callables a Conversation may invoke, and only after Admission says so. The handler never sees a request that was not granted.
tools = Tools.new
tools.register(:cite) { |args, convo| ... }
convo = Conversation.new(engine, allow: { cite: %i[data] }, tools: tools)
convo.ask('Cite the partition table.')
convo.invoke(:cite, arguments: page)
Defined Under Namespace
Classes: Entry, Transaction
Instance Method Summary collapse
- #call(_name, _arguments, _conversation) ⇒ Object
- #dispatch(name, arguments, conversation, authorization:, transaction: nil) ⇒ Object
- #dup ⇒ Object
-
#initialize(handlers = {}) ⇒ Tools
constructor
A new instance of Tools.
- #key?(name) ⇒ Boolean
- #names ⇒ Object
- #prepare_transaction(name, arguments, conversation, call:) ⇒ Object
- #readonly?(name) ⇒ Boolean
- #register(name, callable = nil, readonly: false, output: Origin.tool, &block) ⇒ Object
- #register_transactional(name, prepare:, commit:, rollback:, output: Origin.tool) ⇒ Object
- #rollback_transaction(prepared, conversation) ⇒ Object
- #to_h ⇒ Object
- #transactional?(name) ⇒ Boolean
Constructor Details
#initialize(handlers = {}) ⇒ Tools
Returns a new instance of Tools.
46 47 48 49 |
# File 'lib/vangrail/tools.rb', line 46 def initialize(handlers = {}) @handlers = {} handlers.each { |name, fn| register(name, fn) } end |
Instance Method Details
#call(_name, _arguments, _conversation) ⇒ Object
93 94 95 |
# File 'lib/vangrail/tools.rb', line 93 def call(_name, _arguments, _conversation) raise PrivilegeError, 'use Conversation#invoke' end |
#dispatch(name, arguments, conversation, authorization:, transaction: nil) ⇒ Object
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/vangrail/tools.rb', line 119 def dispatch(name, arguments, conversation, authorization:, transaction: nil) monitor = conversation&.monitor unless &.call&.tool == name.to_sym && monitor&.claim() raise PrivilegeError, 'tool dispatch requires a live authorization' end value = if transaction commit_transaction(name, transaction, conversation) else fire(name, arguments, conversation) end label_output(name, value, .call) rescue StandardError rollback_transaction(transaction, conversation) if transaction&.prepared? raise end |
#dup ⇒ Object
136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/vangrail/tools.rb', line 136 def dup copy = self.class.new @handlers.each do |name, entry| if entry.transaction copy.register_transactional(name, output: entry.output, **entry.transaction.to_h) else copy.register(name, entry.handler, readonly: entry.readonly, output: entry.output) end end copy end |
#key?(name) ⇒ Boolean
77 78 79 |
# File 'lib/vangrail/tools.rb', line 77 def key?(name) @handlers.key?(name.to_sym) end |
#names ⇒ Object
85 86 87 |
# File 'lib/vangrail/tools.rb', line 85 def names @handlers.keys end |
#prepare_transaction(name, arguments, conversation, call:) ⇒ Object
97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/vangrail/tools.rb', line 97 def prepare_transaction(name, arguments, conversation, call:) entry = @handlers[name.to_sym] return nil unless call.transaction? && entry&.transaction payload = entry.transaction.prepare.call(arguments, conversation) PreparedTransaction.new(call, payload).tap do |prepared| conversation.monitor.prepare_transaction(call, prepared) end rescue StandardError entry.transaction.rollback.call(payload, conversation) if defined?(payload) && entry&.transaction raise end |
#readonly?(name) ⇒ Boolean
81 82 83 |
# File 'lib/vangrail/tools.rb', line 81 def readonly?(name) !!@handlers[name.to_sym]&.readonly end |
#register(name, callable = nil, readonly: false, output: Origin.tool, &block) ⇒ Object
51 52 53 54 55 56 57 58 |
# File 'lib/vangrail/tools.rb', line 51 def register(name, callable = nil, readonly: false, output: Origin.tool, &block) fn = callable || block raise ArgumentError, "tool #{name} needs a callable" unless fn.respond_to?(:call) output_label = output.is_a?(Label) ? output : Label.new(provenance: output) @handlers[name.to_sym] = Entry.new(handler: fn, readonly: readonly, output: output_label) self end |
#register_transactional(name, prepare:, commit:, rollback:, output: Origin.tool) ⇒ Object
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/vangrail/tools.rb', line 60 def register_transactional(name, prepare:, commit:, rollback:, output: Origin.tool) callbacks = { prepare: prepare, commit: commit, rollback: rollback } callbacks.each do |phase, callback| raise ArgumentError, "transaction #{phase} needs a callable" unless callback.respond_to?(:call) end output_label = output.is_a?(Label) ? output : Label.new(provenance: output) contract = Transaction.new(**callbacks).freeze @handlers[name.to_sym] = Entry.new( handler: nil, readonly: false, output: output_label, transaction: contract, ) self end |
#rollback_transaction(prepared, conversation) ⇒ Object
110 111 112 113 114 115 116 117 |
# File 'lib/vangrail/tools.rb', line 110 def rollback_transaction(prepared, conversation) return unless prepared&.prepared? entry = @handlers.fetch(prepared.tool) entry.transaction.rollback.call(prepared.payload, conversation) prepared.rollback! conversation.monitor.finish_transaction(prepared, committed: false) end |
#to_h ⇒ Object
148 149 150 |
# File 'lib/vangrail/tools.rb', line 148 def to_h @handlers.transform_values(&:handler) end |
#transactional?(name) ⇒ Boolean
89 90 91 |
# File 'lib/vangrail/tools.rb', line 89 def transactional?(name) !@handlers[name.to_sym]&.transaction.nil? end |