Class: LittleGhost::CodeMode::Javascript::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/little_ghost/code_mode/javascript/client.rb

Overview

:nodoc: all

Defined Under Namespace

Classes: Program

Constant Summary collapse

MAX_BUFFERED_OUTPUT_BYTES =
4 * 1024 * 1024
MAX_CAPTURED_STDERR_BYTES =
64 * 1024
SHUTDOWN_TIMEOUT =
0.5
TERMINATION_TIMEOUT =
1.0

Instance Method Summary collapse

Constructor Details

#initialize(session_factory:) ⇒ Client

Returns a new instance of Client.



174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/little_ghost/code_mode/javascript/client.rb', line 174

def initialize(session_factory:)
  unless session_factory.respond_to?(:call)
    raise ArgumentError, "Code-mode process-session factory must be callable"
  end
  @session_factory = session_factory

  @programs = {}
  @programs_mutex = Mutex.new
  @process_mutex = Mutex.new
  @writer_mutex = Mutex.new
  @closed = false
  @failure = nil
end

Instance Method Details

#closeObject



250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/little_ghost/code_mode/javascript/client.rb', line 250

def close
  process = @writer_mutex.synchronize do
    @process_mutex.synchronize do
      return if @closed

      @closed = true
      current = process_state
      clear_process_state
      current
    end
  end
  fail_all_programs("Code-mode client closed")
  stop_process(process)
end

#close_owner(owner) ⇒ Object



226
227
228
229
230
231
232
233
# File 'lib/little_ghost/code_mode/javascript/client.rb', line 226

def close_owner(owner)
  programs = @programs_mutex.synchronize { @programs.values.select { |program| program.owner.equal?(owner) } }
  programs.each do |program|
    terminate(owner:, program_id: program.id)
  rescue LittleGhost::Error, IOError, SystemCallError
    release_program(program)
  end
end

#complete_tool_call(program_id:, call_id:, ok:, value: nil, error: nil) ⇒ Object



235
236
237
238
239
240
241
242
243
# File 'lib/little_ghost/code_mode/javascript/client.rb', line 235

def complete_tool_call(program_id:, call_id:, ok:, value: nil, error: nil)
  program = @programs_mutex.synchronize { @programs[program_id.to_s] }
  return unless program

  send_message(
    {type: "tool_result", program_id:, call_id:, ok:, value:, error:},
    program:
  )
end

#fail_program(owner:, program_id:, error:) ⇒ Object



245
246
247
248
# File 'lib/little_ghost/code_mode/javascript/client.rb', line 245

def fail_program(owner:, program_id:, error:)
  program = owned_program(owner, program_id)
  request_program_failure(program, error.message)
end

#observe(owner:, program_id:, timeout:, max_tokens:, context: nil) ⇒ Object



204
205
206
207
208
209
# File 'lib/little_ghost/code_mode/javascript/client.rb', line 204

def observe(owner:, program_id:, timeout:, max_tokens:, context: nil)
  program = owned_program(owner, program_id)
  result = program.observe(timeout:, max_tokens:, context:)
  release_program(program) if program.terminal?
  result
end

#start_program(owner:, dispatcher:, source:, tools:, program_id: SecureRandom.uuid) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/little_ghost/code_mode/javascript/client.rb', line 188

def start_program(owner:, dispatcher:, source:, tools:, program_id: SecureRandom.uuid)
  program = nil
  send_message({type: "execute", program_id:, source:, tools:}) do |generation|
    program = Program.new(id: program_id, owner:, dispatcher:, generation:)
    @programs_mutex.synchronize do
      raise LittleGhost::ToolError, "Code-mode client is closed" if @closed

      @programs[program.id] = program
    end
  end
  program.id
rescue
  @programs_mutex.synchronize { @programs.delete(program&.id) }
  raise
end

#terminate(owner:, program_id:, max_tokens: 10_000) ⇒ Object



211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/little_ghost/code_mode/javascript/client.rb', line 211

def terminate(owner:, program_id:, max_tokens: 10_000)
  program = owned_program(owner, program_id)
  send_message({type: "terminate", program_id: program.id}, program:) unless program.terminal?
  result = program.observe(timeout: TERMINATION_TIMEOUT, max_tokens:)
  unless program.terminal?
    process_failed(
      RuntimeError.new("Code-mode host did not acknowledge termination"),
      generation: program.generation
    )
    result = program.wait_until_terminal(timeout: TERMINATION_TIMEOUT + Javascript::Session::CLEANUP_TIMEOUT)
  end
  release_program(program)
  result
end