Class: Kward::PanServer

Inherits:
Object
  • Object
show all
Defined in:
lib/kward/pan/server.rb

Constant Summary collapse

DEFAULT_HOST =
"0.0.0.0"
DEFAULT_PORT =
8765
MAX_REQUEST_BODY_BYTES =
64 * 1024

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(client:, working_directory:, config: ConfigFiles.read_config, config_dir: ConfigFiles.config_dir, output: $stderr) ⇒ PanServer

Returns a new instance of PanServer.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/kward/pan/server.rb', line 24

def initialize(client:, working_directory:, config: ConfigFiles.read_config, config_dir: ConfigFiles.config_dir, output: $stderr)
  @client = client
  @output = output
  @workspace = Workspace.new(root: working_directory)
  @config = pan_config(config)
  @host = @config.fetch("host", DEFAULT_HOST).to_s
  @port = positive_port(@config.fetch("port", DEFAULT_PORT))
  @username = @config["username"].to_s
  @password = @config["password"].to_s
  raise "Pan mode requires pan_mode.username and pan_mode.password in #{ConfigFiles.config_path}" if @username.empty? || @password.empty?

  @session_store = SessionStore.new(config_dir: config_dir, cwd: @workspace.root.to_s)
  @session = @session_store.create
  @conversation = Conversation.new(
    workspace_root: @workspace.root.to_s,
    model: (@client.current_model if @client.respond_to?(:current_model)),
    reasoning_effort: (@client.current_reasoning_effort if @client.respond_to?(:current_reasoning_effort))
  )
  @session.attach(@conversation)
  @agent = Agent.new(
    client: @client,
    tool_registry: ToolRegistry.new(workspace: @workspace, ask_user_question_enabled: false),
    conversation: @conversation
  )
  @prompt_queue = Queue.new
  @subscribers = []
  @subscribers_mutex = Mutex.new
  @worker_started = false
  @active = false
  @state_mutex = Mutex.new
end

Instance Attribute Details

#hostObject (readonly)

Returns the value of attribute host.



56
57
58
# File 'lib/kward/pan/server.rb', line 56

def host
  @host
end

#portObject (readonly)

Returns the value of attribute port.



56
57
58
# File 'lib/kward/pan/server.rb', line 56

def port
  @port
end

#sessionObject (readonly)

Returns the value of attribute session.



56
57
58
# File 'lib/kward/pan/server.rb', line 56

def session
  @session
end

#workspaceObject (readonly)

Returns the value of attribute workspace.



56
57
58
# File 'lib/kward/pan/server.rb', line 56

def workspace
  @workspace
end

Instance Method Details

#enqueue_prompt(prompt) ⇒ Object



82
83
84
85
86
87
88
89
90
# File 'lib/kward/pan/server.rb', line 82

def enqueue_prompt(prompt)
  text = prompt.to_s
  return { ok: false, error: "Prompt is required" } if text.strip.empty?

  queued_at = Time.now.utc.iso8601(3)
  @prompt_queue << { prompt: text, queued_at: queued_at }
  broadcast("queue", queue_payload)
  { ok: true, queued: @prompt_queue.size, active: active? }
end

#runObject



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/kward/pan/server.rb', line 58

def run
  start_worker
  @server = TCPServer.new(@host, @port)
  actual_port = @server.addr[1]
  @output.puts "Kward pan mode listening on http://#{display_host}:#{actual_port}"
  @output.puts "Workspace: #{@workspace.root}"
  @output.puts "Session: #{@session.path}"

  loop do
    socket = @server.accept
    Thread.new(socket) { |client_socket| handle_client(client_socket) }
  end
rescue Interrupt
  @output.puts "\nPan mode stopped."
ensure
  stop
end

#stopObject



76
77
78
79
80
# File 'lib/kward/pan/server.rb', line 76

def stop
  @server&.close unless @server&.closed?
rescue IOError
  nil
end

#transcript_itemsObject



92
93
94
# File 'lib/kward/pan/server.rb', line 92

def transcript_items
  RPC::TranscriptNormalizer.new(@conversation.messages).normalize.flat_map { |message| pan_transcript_items(message) }
end