Class: Ask::AppServer::AgentAdapter

Inherits:
Object
  • Object
show all
Defined in:
lib/ask/app_server/agent_adapter.rb

Overview

Wraps an Ask::Agent::Session and translates its events into the app-server protocol event format via EventTranslator.

Each wrapper is associated with one session and maintains an EventTranslator that app-server clients poll or subscribe to.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model:, tools: nil, system_prompt: nil, agent_dir: nil, **session_opts) ⇒ AgentAdapter

Returns a new instance of AgentAdapter.



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/ask/app_server/agent_adapter.rb', line 26

def initialize(model:, tools: nil, system_prompt: nil, agent_dir: nil, **session_opts)
  @model = model
  @system_prompt = system_prompt
  @tools = resolve_tools(tools)
  @session_opts = session_opts
  @agent_dir = agent_dir
  @session = nil
  @translator = nil
  @session_id = nil
  @running = false
  @running_mutex = Mutex.new
  @run_thread = nil
  @abort_requested = false
  @created_at = Time.now
  @logger = Logger.new($stdout, level: ENV["DEBUG"] ? Logger::DEBUG : Logger::WARN)
end

Instance Attribute Details

#created_atObject (readonly)

When the session was created.



24
25
26
# File 'lib/ask/app_server/agent_adapter.rb', line 24

def created_at
  @created_at
end

#runningObject (readonly)

Whether a turn is currently in progress.



21
22
23
# File 'lib/ask/app_server/agent_adapter.rb', line 21

def running
  @running
end

#sessionObject (readonly)

The underlying ask-agent session.



12
13
14
# File 'lib/ask/app_server/agent_adapter.rb', line 12

def session
  @session
end

#session_idObject (readonly)

The session ID (same as ask-agent session id).



18
19
20
# File 'lib/ask/app_server/agent_adapter.rb', line 18

def session_id
  @session_id
end

#translatorObject (readonly)

The event translator that accumulates protocol events.



15
16
17
# File 'lib/ask/app_server/agent_adapter.rb', line 15

def translator
  @translator
end

Instance Method Details

#abort_turn!Object

Request abort of the current turn.



100
101
102
103
# File 'lib/ask/app_server/agent_adapter.rb', line 100

def abort_turn!
  @abort_requested = true
  @session&.abort if @session
end

#drain_eventsObject

Drain and return pending events.



147
148
149
# File 'lib/ask/app_server/agent_adapter.rb', line 147

def drain_events
  @translator&.drain_events || []
end

#events_after(after_seq) ⇒ Object

Events after a given sequence number.



157
158
159
# File 'lib/ask/app_server/agent_adapter.rb', line 157

def events_after(after_seq)
  pending_events.select { |e| e[:seq] > after_seq }
end

#idle?Boolean

Whether this session is idle (no turn running).

Returns:

  • (Boolean)


116
117
118
# File 'lib/ask/app_server/agent_adapter.rb', line 116

def idle?
  !@running
end

#inject_message(content) ⇒ Object

Inject a message into a running session (mid-execution). Aborts the current turn; the message will be processed when the next turn starts. Note: ask-agent doesn't natively support mid-execution injection. We abort and queue the message for the next run.



125
126
127
128
129
130
131
132
133
134
# File 'lib/ask/app_server/agent_adapter.rb', line 125

def inject_message(content)
  if @running
    abort_turn!
    # The caller should wait for idle, then call send_message again
    false
  else
    send_message(content)
    true
  end
end

#last_seqObject

Last sequence number.



152
153
154
# File 'lib/ask/app_server/agent_adapter.rb', line 152

def last_seq
  @translator&.last_seq || 0
end

#pending_eventsObject

All events since last drain.



142
143
144
# File 'lib/ask/app_server/agent_adapter.rb', line 142

def pending_events
  @translator&.pending_events || []
end

#resume(session) ⇒ Object

Resume an existing session (re-attach event handler).



66
67
68
69
70
71
72
# File 'lib/ask/app_server/agent_adapter.rb', line 66

def resume(session)
  @session = session
  @session_id = session.id
  @translator = EventTranslator.new(@session_id)
  @session.on_event { |event| handle_agent_event(event) }
  @session_id
end

#send_message(content) ⇒ Object

Send a message and start processing. Runs in a background thread. The caller should poll or subscribe to receive events.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/ask/app_server/agent_adapter.rb', line 76

def send_message(content)
  raise "Session not started" unless @session
  raise "Session already busy" if @running

  @running_mutex.synchronize do
    @abort_requested = false
    @running = true
  end

  @run_thread = Thread.new do
    begin
      @session.run(content)
    rescue => e
      # Agent may have been aborted — that's fine
      @logger.debug("Agent run error: #{e.message}") if ENV["DEBUG"]
    ensure
      @running_mutex.synchronize { @running = false }
    end
  end

  true
end

#start_sessionObject

Start a new ask-agent session. Returns the session ID.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/ask/app_server/agent_adapter.rb', line 45

def start_session
  opts = @session_opts.dup

  # Extract hooks from session opts and pass them to Session
  hooks = opts.delete(:hooks) || {}

  @session = Ask::Agent::Session.new(
    model: @model,
    tools: @tools,
    system_prompt: @system_prompt,
    agent_dir: @agent_dir,
    hooks: hooks,
    **opts
  )
  @session_id = @session.id
  @translator = EventTranslator.new(@session_id)
  @session.on_event { |event| handle_agent_event(event) }
  @session_id
end

#streaming_textObject

The accumulated streaming text from the current/ last turn.



137
138
139
# File 'lib/ask/app_server/agent_adapter.rb', line 137

def streaming_text
  @translator&.instance_variable_get(:@streaming_text).to_s
end

#wait_for_turn(timeout: 600) ⇒ Object

Wait for the current turn to complete (with timeout). Returns true if completed, false if timed out.



107
108
109
110
111
112
113
# File 'lib/ask/app_server/agent_adapter.rb', line 107

def wait_for_turn(timeout: 600)
  thread = @run_thread
  return true unless thread

  thread.join(timeout)
  !thread.alive?
end