1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
# frozen_string_literal: true
require 'babosa'
require 'fileutils'
require_relative '../logic/helpers/hash'
require_relative '../logic/cartridge/streaming'
require_relative '../logic/cartridge/interaction'
require_relative '../components/storage'
require_relative '../components/adapter'
module NanoBot
module Controllers
STREAM_TIMEOUT_IN_SECONDS = 5
class Session
attr_accessor :stream
def initialize(provider:, cartridge:, state: nil, stream: $stdout)
@stream = stream
@provider = provider
@cartridge = cartridge
@stateless = state.nil? || state.strip == '-' || state.strip.empty?
if @stateless
@state = { history: [] }
else
@state_path = Components::Storage.build_path_and_ensure_state_file!(
state.strip, @cartridge
)
@state = load_state
end
end
def state
{ state: { path: @state_path, content: @state } }
end
def load_state
@state = Logic::Helpers::Hash.symbolize_keys(JSON.parse(File.read(@state_path)))
end
def store_state!
File.write(@state_path, JSON.generate(@state))
end
def boot(mode:)
return unless Logic::Helpers::Hash.fetch(@cartridge, %i[behaviors boot instruction])
behavior = Logic::Helpers::Hash.fetch(@cartridge, %i[behaviors boot]) || {}
input = { behavior:, history: [] }
process(input, mode:)
end
def evaluate_and_print(message, mode:)
behavior = Logic::Helpers::Hash.fetch(@cartridge, %i[behaviors interaction]) || {}
@state[:history] << {
who: 'user',
mode: mode.to_s,
input: message,
message: Components::Adapter.apply(
:input, Logic::Cartridge::Interaction.input(@cartridge, mode.to_sym, message)
)
}
input = { behavior:, history: @state[:history] }
process(input, mode:)
end
def process(input, mode:)
prefix = Logic::Cartridge::Affixes.get(@cartridge, mode.to_sym, :output, :prefix)
suffix = Logic::Cartridge::Affixes.get(@cartridge, mode.to_sym, :output, :suffix)
interface = Logic::Helpers::Hash.fetch(@cartridge, [:interfaces, mode.to_sym]) || {}
streaming = Logic::Cartridge::Streaming.enabled?(@cartridge, mode.to_sym)
input[:interface] = interface
updated_at = Time.now
ready = false
@provider.evaluate(input) do |output, finished|
updated_at = Time.now
if finished
event = Marshal.load(Marshal.dump(output))
output = Logic::Cartridge::Interaction.output(
@cartridge, mode.to_sym, output, streaming, finished
)
output[:message] = Components::Adapter.apply(:output, output[:message])
event[:mode] = mode.to_s
event[:output] = "#{prefix}#{output[:message]}#{suffix}"
@state[:history] << event
self.print(output[:message]) unless streaming
ready = true
flush
elsif streaming
self.print(output[:message])
end
end
until ready
seconds = (Time.now - updated_at).to_i
raise StandardError, 'The stream has become unresponsive.' if seconds >= STREAM_TIMEOUT_IN_SECONDS
end
store_state! unless @stateless
end
def flush
@stream.flush
end
def print(content)
@stream.write(content)
end
end
end
end
|