Class: Kward::PluginChatRuntime
- Inherits:
-
Object
- Object
- Kward::PluginChatRuntime
- Defined in:
- lib/kward/plugins/chat_runtime.rb
Overview
Coordinates plugin chat drivers without depending on RPC or the terminal UI. Frontends use this runtime for queuing, cancellation, event replay, and transcript access while the plugin remains responsible for its own storage, model settings, and domain behavior.
Defined Under Namespace
Constant Summary collapse
- EVENT_LIMIT =
1_000- WORKER_STOP =
Object.new.freeze
Instance Method Summary collapse
- #cancel_turn(turn_id:) ⇒ Object
- #chat(chat_id) ⇒ Object
-
#initialize(client:, plugin_registry_provider: nil, message_normalizer: nil) ⇒ PluginChatRuntime
constructor
A new instance of PluginChatRuntime.
-
#input_with_attachments(input, attachments, capabilities: PluginChatCapabilities.legacy) ⇒ Object
Converts normalized image attachment hashes into the input shape accepted by plugin chat drivers.
- #list_turns(chat_id: nil, active: false) ⇒ Object
- #open(type_id:, surface: :rpc, scope_key: nil, descriptor: {}, workspace_root: Dir.pwd) ⇒ Object
- #shutdown ⇒ Object
- #start_turn(chat_id:, input:, display_input: nil, context: nil) ⇒ Object
- #subscribe_events(&block) ⇒ Object
- #supported_types(surface: :rpc) ⇒ Object
- #turn_events(turn_id:, after_sequence: 0) ⇒ Object
- #turn_status(turn_id:) ⇒ Object
Constructor Details
#initialize(client:, plugin_registry_provider: nil, message_normalizer: nil) ⇒ PluginChatRuntime
Returns a new instance of PluginChatRuntime.
51 52 53 54 55 56 57 58 59 60 |
# File 'lib/kward/plugins/chat_runtime.rb', line 51 def initialize(client:, plugin_registry_provider: nil, message_normalizer: nil) @client = client @plugin_registry_provider = plugin_registry_provider @message_normalizer = @chats = {} @turns = {} @event_listeners = [] @mutex = Mutex.new @shutdown = false end |
Instance Method Details
#cancel_turn(turn_id:) ⇒ Object
113 114 115 116 117 118 119 120 121 122 |
# File 'lib/kward/plugins/chat_runtime.rb', line 113 def cancel_turn(turn_id:) turn = fetch_turn(turn_id) queued = turn.mutex.synchronize do turn.cancellation.cancel! turn.status == "queued" end emit_event(turn, "turnCancelRequested", {}) finish_turn(turn, "canceled") if queued turn end |
#chat(chat_id) ⇒ Object
87 88 89 |
# File 'lib/kward/plugins/chat_runtime.rb', line 87 def chat(chat_id) @mutex.synchronize { @chats[chat_id.to_s] } end |
#input_with_attachments(input, attachments, capabilities: PluginChatCapabilities.legacy) ⇒ Object
Converts normalized image attachment hashes into the input shape accepted by plugin chat drivers. RPC and transport frontends can normalize their own boundary formats before calling this helper.
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/kward/plugins/chat_runtime.rb', line 162 def (input, , capabilities: PluginChatCapabilities.legacy) = Array() return input.to_s if .empty? if capabilities.declared? && !capabilities.(:image) raise ArgumentError, "plugin chat does not allow image attachments" end [{ type: "text", text: input.to_s }] + .map do || { type: "image", media_type: .fetch(:mimeType) { .fetch("mimeType") }, data: .fetch(:data) { .fetch("data") }, alt: [:alt] || ["alt"] }.compact end end |
#list_turns(chat_id: nil, active: false) ⇒ Object
135 136 137 138 139 140 |
# File 'lib/kward/plugins/chat_runtime.rb', line 135 def list_turns(chat_id: nil, active: false) turns = @mutex.synchronize { @turns.values.dup } turns.select! { |turn| turn.chat_id == chat_id.to_s } if chat_id turns.select! { |turn| %w[queued running].include?(turn.status) } if active turns end |
#open(type_id:, surface: :rpc, scope_key: nil, descriptor: {}, workspace_root: Dir.pwd) ⇒ Object
80 81 82 83 84 85 |
# File 'lib/kward/plugins/chat_runtime.rb', line 80 def open(type_id:, surface: :rpc, scope_key: nil, descriptor: {}, workspace_root: Dir.pwd) type = supported_types(surface: surface).find { |entry| entry.id == type_id.to_s } raise ArgumentError, "Unknown #{surface} plugin chat: #{type_id}" unless type chat_for(type, surface: surface, scope_key: scope_key, descriptor: descriptor, workspace_root: workspace_root) end |
#shutdown ⇒ Object
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/kward/plugins/chat_runtime.rb', line 142 def shutdown chats = @mutex.synchronize do return nil if @shutdown @shutdown = true @chats.values.dup end @mutex.synchronize { @turns.values.dup }.each { |turn| turn.cancellation.cancel! } chats.each do |chat| chat.queue << WORKER_STOP if chat.worker&.alive? chat.worker&.join(0.2) close_chat(chat) end @plugin_registry&.shutdown! unless @plugin_registry_provider nil end |
#start_turn(chat_id:, input:, display_input: nil, context: nil) ⇒ Object
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/kward/plugins/chat_runtime.rb', line 91 def start_turn(chat_id:, input:, display_input: nil, context: nil) chat = fetch_chat(chat_id) turn = Turn.new( id: SecureRandom.uuid, chat_id: chat.id, input: input, display_input: display_input.nil? ? input.to_s : display_input.to_s, context: context || {}, status: "queued", cancellation: Cancellation.new, created_at: Time.now.utc.iso8601(3), events: [], next_sequence: 1, mutex: Mutex.new ) @mutex.synchronize { @turns[turn.id] = turn } chat.queue << turn.id ensure_worker(chat) emit_event(turn, "turnQueued", { status: "queued" }) turn end |
#subscribe_events(&block) ⇒ Object
73 74 75 76 77 78 |
# File 'lib/kward/plugins/chat_runtime.rb', line 73 def subscribe_events(&block) raise ArgumentError, "plugin chat event subscription requires a block" unless block @mutex.synchronize { @event_listeners << block } block end |
#supported_types(surface: :rpc) ⇒ Object
62 63 64 65 66 67 68 69 70 71 |
# File 'lib/kward/plugins/chat_runtime.rb', line 62 def supported_types(surface: :rpc) case surface.to_sym when :rpc plugin_registry.tab_types.select(&:rpc) when :transport plugin_registry.transport_tab_types else raise ArgumentError, "Unknown plugin chat surface: #{surface}" end end |
#turn_events(turn_id:, after_sequence: 0) ⇒ Object
128 129 130 131 132 133 |
# File 'lib/kward/plugins/chat_runtime.rb', line 128 def turn_events(turn_id:, after_sequence: 0) turn = fetch_turn(turn_id) turn.mutex.synchronize do turn.events.select { |event| event[:sequence].to_i > after_sequence.to_i } end end |
#turn_status(turn_id:) ⇒ Object
124 125 126 |
# File 'lib/kward/plugins/chat_runtime.rb', line 124 def turn_status(turn_id:) fetch_turn(turn_id) end |