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
133
|
# frozen_string_literal: true
require 'babosa'
require 'fileutils'
require_relative '../logic/helpers/hash'
module NanoBot
module Controllers
STREAM_TIMEOUT_IN_SECONDS = 5
class Session
def initialize(provider:, cartridge:, state: nil)
@provider = provider
@cartridge = cartridge
@output = $stdout
@stateless = state.nil? || state.strip == '-' || state.strip.empty?
if @stateless
@state = { history: [] }
else
build_path_and_ensure_state_file!(state.strip)
@state = load_state
end
end
def debug
pp({
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 build_path_and_ensure_state_file!(key)
path = Logic::Helpers::Hash.fetch(@cartridge, %i[state directory])
path = "#{user_home!.sub(%r{/$}, '')}/.local/share/.nano-bots" if path.nil? || path.empty?
path = "#{path.sub(%r{/$}, '')}/nano-bots-rb/#{@cartridge[:name].to_slug.normalize}"
path = "#{path}/#{@cartridge[:version].to_slug.normalize}/#{key.to_slug.normalize}"
path = "#{path}/state.json"
@state_path = path
FileUtils.mkdir_p(File.dirname(@state_path))
File.write(@state_path, JSON.generate({ key:, history: [] })) unless File.exist?(@state_path)
end
def user_home!
[Dir.home, `echo ~`.strip, '~'].find do |candidate|
!candidate.nil? && !candidate.empty?
end
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', message: })
input = { behavior:, history: @state[:history] }
process(input, mode:)
end
def process(input, mode:)
streaming = @provider.settings[:stream] && Logic::Helpers::Hash.fetch(
@cartridge, [:interfaces, mode.to_sym, :stream]
)
interface = Logic::Helpers::Hash.fetch(@cartridge, [:interfaces, mode.to_sym]) || {}
input[:interface] = interface
updated_at = Time.now
ready = false
@provider.evaluate(input) do |output, finished|
updated_at = Time.now
if finished
@state[:history] << output
self.print(output[:message]) unless streaming
unless Logic::Helpers::Hash.fetch(@cartridge, [:interfaces, mode.to_sym, :postfix]).nil?
self.print(Logic::Helpers::Hash.fetch(@cartridge, [:interfaces, mode.to_sym, :postfix]))
end
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
@output.flush
end
def print(content)
@output.write(content)
end
end
end
end
|